简体   繁体   English

ASP.NET Core 返回带有状态代码的 JSON

[英]ASP.NET Core return JSON with status code

I'm looking for the correct way to return JSON with a HTTP status code in my .NET Core Web API controller.我正在寻找在我的 .NET Core Web API 控制器中返回带有 HTTP 状态代码的 JSON 的正确方法。 I use to use it like this:我习惯这样使用它:

public IHttpActionResult GetResourceData()
{
    return this.Content(HttpStatusCode.OK, new { response = "Hello"});
}

This was in a 4.6 MVC application but now with .NET Core I don't seem to have this IHttpActionResult I have ActionResult and using like this:这是在 4.6 MVC 应用程序中,但现在使用 .NET Core 我似乎没有这个IHttpActionResult我有ActionResult并使用如下:

public ActionResult IsAuthenticated()
{
    return Ok(Json("123"));
}

But the response from the server is weird, as in the image below:但是服务器的响应很奇怪,如下图所示:

在此处输入图片说明

I just want the Web API controller to return JSON with a HTTP status code like I did in Web API 2.我只希望 Web API 控制器像我在 Web API 2 中所做的那样返回带有 HTTP 状态代码的 JSON。

The most basic version responding with a JsonResult is:使用JsonResult响应的最基本版本是:

// GET: api/authors
[HttpGet]
public JsonResult Get()
{
    return Json(_authorRepository.List());
}

However, this isn't going to help with your issue because you can't explicitly deal with your own response code.但是,这对您的问题没有帮助,因为您无法明确处理自己的响应代码。

The way to get control over the status results, is you need to return a ActionResult which is where you can then take advantage of the StatusCodeResult type.控制状态结果的方法是,您需要返回一个ActionResult ,然后您可以在其中利用StatusCodeResult类型。

for example:例如:

// GET: api/authors/search?namelike=foo
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
    var result = _authorRepository.GetByNameSubstring(namelike);
    if (!result.Any())
    {
        return NotFound(namelike);
    }
    return Ok(result);
}

Note both of these above examples came from a great guide available from Microsoft Documentation: Formatting Response Data请注意,上述两个示例都来自 Microsoft 文档中的一个很好的指南: 格式化响应数据


Extra Stuff额外的东西

The issue I come across quite often is that I wanted more granular control over my WebAPI rather than just go with the defaults configuration from the "New Project" template in VS.我经常遇到的问题是,我希望对我的 WebAPI 进行更精细的控制,而不仅仅是使用 VS 中“新项目”模板中的默认配置。

Let's make sure you have some of the basics down...让我们确保你掌握了一些基础知识......

Step 1: Configure your Service第 1 步:配置您的服务

In order to get your ASP.NET Core WebAPI to respond with a JSON Serialized Object along full control of the status code, you should start off by making sure that you have included the AddMvc() service in your ConfigureServices method usually found in Startup.cs .为了让您的 ASP.NET Core WebAPI 响应一个 JSON 序列化对象并完全控制状态代码,您应该首先确保您已在通常在Startup.cs找到的ConfigureServices方法中包含AddMvc()服务Startup.cs .

It's important to note that AddMvc() will automatically include the Input/Output Formatter for JSON along with responding to other request types.请务必注意, AddMvc()将自动包含 JSON 的输入/输出格式化程序以及响应其他请求类型。

If your project requires full control and you want to strictly define your services, such as how your WebAPI will behave to various request types including application/json and not respond to other request types (such as a standard browser request), you can define it manually with the following code:如果您的项目需要完全控制并且您想严格定义您的服务,例如您的 WebAPI 将如何处理包括application/json在内的各种请求类型而不响应其他请求类型(例如标准浏览器请求),您可以定义它手动使用以下代码:

public void ConfigureServices(IServiceCollection services)
{
    // Build a customized MVC implementation, without using the default AddMvc(), instead use AddMvcCore().
    // https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs

    services
        .AddMvcCore(options =>
        {
            options.RequireHttpsPermanent = true; // does not affect api requests
            options.RespectBrowserAcceptHeader = true; // false by default
            //options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();

            //remove these two below, but added so you know where to place them...
            options.OutputFormatters.Add(new YourCustomOutputFormatter()); 
            options.InputFormatters.Add(new YourCustomInputFormatter());
        })
        //.AddApiExplorer()
        //.AddAuthorization()
        .AddFormatterMappings()
        //.AddCacheTagHelper()
        //.AddDataAnnotations()
        //.AddCors()
        .AddJsonFormatters(); // JSON, or you can build your own custom one (above)
}

You will notice that I have also included a way for you to add your own custom Input/Output formatters, in the event you may want to respond to another serialization format (protobuf, thrift, etc).您会注意到我还提供了一种方法来添加您自己的自定义输入/输出格式化程序,以防您可能想要响应另一种序列化格式(protobuf、thrift 等)。

The chunk of code above is mostly a duplicate of the AddMvc() method.上面的代码块主要是AddMvc()方法的副本。 However, we are implementing each "default" service on our own by defining each and every service instead of going with the pre-shipped one with the template.但是,我们通过定义每个服务而不是使用带有模板的预装服务来实现我们自己的每个“默认”服务。 I have added the repository link in the code block, or you can check out AddMvc() from the GitHub repository.我在代码块中添加了存储库链接,或者您可以从 GitHub 存储库中查看AddMvc() . .

Note that there are some guides that will try to solve this by "undoing" the defaults, rather than just not implementing it in the first place... If you factor in that we're now working with Open Source, this is redundant work, bad code and frankly an old habit that will disappear soon.请注意,有一些指南会尝试通过“撤消”默认值来解决此问题,而不仅仅是一开始就不实施它……如果您考虑到我们现在正在使用开源,这是多余的工作,糟糕的代码,坦率地说,一个很快就会消失的旧习惯。


Step 2: Create a Controller第 2 步:创建控制器

I'm going to show you a really straight-forward one just to get your question sorted.我将向您展示一个非常简单的方法,只是为了解决您的问题。

public class FooController
{
    [HttpPost]
    public async Task<IActionResult> Create([FromBody] Object item)
    {
        if (item == null) return BadRequest();

        var newItem = new Object(); // create the object to return
        if (newItem != null) return Ok(newItem);

        else return NotFound();
    }
}

Step 3: Check your Content-Type and Accept第 3 步:检查您的Content-TypeAccept

You need to make sure that your Content-Type and Accept headers in your request are set properly.您需要确保您的请求中的Content-TypeAccept标头设置正确。 In your case (JSON), you will want to set it up to be application/json .在您的情况(JSON)中,您需要将其设置为application/json

If you want your WebAPI to respond as JSON as default, regardless of what the request header is specifying you can do that in a couple ways .如果您希望您的 WebAPI 作为默认响应 JSON,无论请求标头指定什么,您都可以通过几种方式来实现

Way 1 As shown in the article I recommended earlier ( Formatting Response Data ) you could force a particular format at the Controller/Action level.方式 1如我之前推荐的文章( 格式化响应数据)所示,您可以在控制器/操作级别强制使用特定格式。 I personally don't like this approach... but here it is for completeness:我个人不喜欢这种方法......但这里是为了完整性:

Forcing a Particular Format If you would like to restrict the response formats for a specific action you can, you can apply the [Produces] filter.强制特定格式如果您想限制特定操作的响应格式,您可以应用 [Produces] 过滤器。 The [Produces] filter specifies the response formats for a specific action (or controller). [Produces] 过滤器指定特定操作(或控制器)的响应格式。 Like most Filters, this can be applied at the action, controller, or global scope.像大多数过滤器一样,这可以应用于操作、控制器或全局范围。

 [Produces("application/json")] public class AuthorsController

The [Produces] filter will force all actions within the AuthorsController to return JSON-formatted responses, even if other formatters were configured for the application and the client provided an Accept header requesting a different, available format. [Produces]过滤器将强制AuthorsController所有操作返回 JSON 格式的响应,即使为应用程序配置了其他格式化程序并且客户端提供了请求不同的可用格式的Accept标头。

Way 2 My preferred method is for the WebAPI to respond to all requests with the format requested.方式 2我的首选方法是让 WebAPI 以请求的格式响应所有请求。 However, in the event that it doesn't accept the requested format, then fall-back to a default (ie. JSON)但是,如果它不接受请求的格式,则回退到默认值(即 JSON)

First, you'll need to register that in your options (we need to rework the default behavior, as noted earlier)首先,您需要在您的选项中注册它(我们需要重新设计默认行为,如前所述)

options.RespectBrowserAcceptHeader = true; // false by default

Finally, by simply re-ordering the list of the formatters that were defined in the services builder, the web host will default to the formatter you position at the top of the list (ie position 0).最后,通过简单地重新排序在服务构建器中定义的格式化程序列表,Web 主机将默认为您放置在列表顶部的格式化程序(即位置 0)。

More information can be found in this .NET Web Development and Tools Blog entry可以在此.NET Web 开发和工具博客条目中找到更多信息

You have predefined methods for most common status codes.您为最常见的状态代码预定义了方法。

  • Ok(result) returns 200 with response Ok(result)返回200并带有响应
  • CreatedAtRoute returns 201 + new resource URL CreatedAtRoute返回201 + 新资源 URL
  • NotFound returns 404 NotFound返回404
  • BadRequest returns 400 etc. BadRequest返回400等。

See BaseController.cs and Controller.cs for a list of all methods.有关所有方法的列表,请参阅BaseController.csController.cs

But if you really insist you can use StatusCode to set a custom code, but you really shouldn't as it makes code less readable and you'll have to repeat code to set headers (like for CreatedAtRoute ).但是,如果您真的坚持可以使用StatusCode来设置自定义代码,但您确实不应该这样做,因为它会降低代码的可读性,并且您必须重复代码来设置标头(例如CreatedAtRoute )。

public ActionResult IsAuthenticated()
{
    return StatusCode(200, "123");
}

With ASP.NET Core 2.0 , the ideal way to return object from Web API (which is unified with MVC and uses same base class Controller ) is使用ASP.NET Core 2.0 ,从Web API (与 MVC 统一并使用相同的基类Controller )返回对象的理想方式是

public IActionResult Get()
{
    return new OkObjectResult(new Item { Id = 123, Name = "Hero" });
}

Notice that请注意

  1. It returns with 200 OK status code (it's an Ok type of ObjectResult )它返回200 OK状态代码(它是一个Ok类型的ObjectResult
  2. It does content negotiation, ie it'll return based on Accept header in request.它进行内容协商,即它会根据请求中的Accept标头返回。 If Accept: application/xml is sent in request, it'll return as XML .如果Accept: application/xml在请求中发送,它将作为XML返回。 If nothing is sent, JSON is default.如果不发送任何内容,则默认为JSON

If it needs to send with specific status code , use ObjectResult or StatusCode instead.如果需要使用特定的状态代码发送,请改用ObjectResultStatusCode Both does the same thing, and supports content negotiation.两者都做同样的事情,并支持内容协商。

return new ObjectResult(new Item { Id = 123, Name = "Hero" }) { StatusCode = 200 };
return StatusCode( 200, new Item { Id = 123, Name = "Hero" });

or even more fine grained with ObjectResult:甚至更细粒度的 ObjectResult:

 Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection myContentTypes = new Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection { System.Net.Mime.MediaTypeNames.Application.Json };
 String hardCodedJson = "{\"Id\":\"123\",\"DateOfRegistration\":\"2012-10-21T00:00:00+05:30\",\"Status\":0}";
 return new ObjectResult(hardCodedJson) { StatusCode = 200, ContentTypes = myContentTypes };

If you specifically want to return as JSON , there are couple of ways如果您特别想作为 JSON 返回,有几种方法

//GET http://example.com/api/test/asjson
[HttpGet("AsJson")]
public JsonResult GetAsJson()
{
    return Json(new Item { Id = 123, Name = "Hero" });
}

//GET http://example.com/api/test/withproduces
[HttpGet("WithProduces")]
[Produces("application/json")]
public Item GetWithProduces()
{
    return new Item { Id = 123, Name = "Hero" };
}

Notice that请注意

  1. Both enforces JSON in two different ways.两者都以两种不同的方式强制执行JSON
  2. Both ignores content negotiation.两者都忽略了内容协商。
  3. First method enforces JSON with specific serializer Json(object) .第一种方法使用特定的序列化程序Json(object)强制 JSON。
  4. Second method does the same by using Produces() attribute (which is a ResultFilter ) with contentType = application/json第二种方法通过使用Produces()属性(这是一个ResultFilter )和contentType = application/json来做同样的ResultFilter

Read more about them in the official docs .官方文档中阅读更多关于它们的信息 Learn about filters here . 在此处了解过滤器

The simple model class that is used in the samples示例中使用的简单模型类

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
}

The easiest way I came up with is :我想出的最简单的方法是:

var result = new Item { Id = 123, Name = "Hero" };

return new JsonResult(result)
{
    StatusCode = StatusCodes.Status201Created // Status code here 
};

This is my easiest solution:这是我最简单的解决方案:

public IActionResult InfoTag()
{
    return Ok(new {name = "Fabio", age = 42, gender = "M"});
}

or或者

public IActionResult InfoTag()
{
    return Json(new {name = "Fabio", age = 42, gender = "M"});
}

Instead of using 404/201 status codes using enum而不是使用枚举使用 404/201 状态代码

     public async Task<IActionResult> Login(string email, string password)
    {
        if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
        { 
            return StatusCode((int)HttpStatusCode.BadRequest, Json("email or password is null")); 
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));

        }
        var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: true, lockoutOnFailure: false);
        if (!passwordSignInResult.Succeeded)
        {
            return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
        }
        return StatusCode((int)HttpStatusCode.OK, Json("Sucess !!!"));
    }

Awesome answers I found here and I also tried this return statement see StatusCode(whatever code you wish) and it worked!!!我在这里找到了很棒的答案,我也尝试了这个返回语句,请参阅StatusCode(whatever code you wish) ,它奏效了!!!

return Ok(new {
                    Token = new JwtSecurityTokenHandler().WriteToken(token),
                    Expiration = token.ValidTo,
                    username = user.FullName,
                    StatusCode = StatusCode(200)
                });

Controller action return types in ASP.NET Core web API 02/03/2020 ASP.NET Core Web API 中的控制器操作返回类型 02/03/2020

6 minutes to read +2 6 分钟阅读 +2

By Scott Addie Link作者:斯科特·艾迪Link

Synchronous action 同步动作

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return product;
}

Asynchronous action 异步操作

[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
    if (product.Description.Contains("XYZ Widget"))
    {
        return BadRequest();
    }

    await _repository.AddProductAsync(product);

    return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}

Please refer below code, You can manage multiple status code with different type JSON请参考以下代码,您可以使用不同类型的 JSON 管理多个状态代码

public async Task<HttpResponseMessage> GetAsync()
{
    try
    {
        using (var entities = new DbEntities())
        {
            var resourceModelList = entities.Resources.Select(r=> new ResourceModel{Build Your Resource Model}).ToList();

            if (resourceModelList.Count == 0)
            {
                return this.Request.CreateResponse<string>(HttpStatusCode.NotFound, "No resources found.");
            }

            return this.Request.CreateResponse<List<ResourceModel>>(HttpStatusCode.OK, resourceModelList, "application/json");
        }
    }
    catch (Exception ex)
    {
        return this.Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong.");
    }
}

What I do in my Asp Net Core Api applications it is to create a class that extends from ObjectResult and provide many constructors to customize the content and the status code.我在 Asp Net Core Api 应用程序中所做的是创建一个从 ObjectResult 扩展的类,并提供许多构造函数来自定义内容和状态代码。 Then all my Controller actions use one of the costructors as appropiate.然后我所有的 Controller 操作都使用其中一个 costructors 作为适当的。 You can take a look at my implementation at: https://github.com/melardev/AspNetCoreApiPaginatedCrud你可以看看我的实现: https : //github.com/melardev/AspNetCoreApiPaginatedCrud

and

https://github.com/melardev/ApiAspCoreEcommerce https://github.com/melardev/ApiAspCoreEcommerce

here is how the class looks like(go to my repo for full code):这是类的样子(请访问我的 repo 以获取完整代码):

public class StatusCodeAndDtoWrapper : ObjectResult
{



    public StatusCodeAndDtoWrapper(AppResponse dto, int statusCode = 200) : base(dto)
    {
        StatusCode = statusCode;
    }

    private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, string message) : base(dto)
    {
        StatusCode = statusCode;
        if (dto.FullMessages == null)
            dto.FullMessages = new List<string>(1);
        dto.FullMessages.Add(message);
    }

    private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, ICollection<string> messages) : base(dto)
    {
        StatusCode = statusCode;
        dto.FullMessages = messages;
    }
}

Notice the base(dto) you replace dto by your object and you should be good to go.注意 base(dto) 你用你的对象替换了 dto ,你应该很高兴。

I got this to work.我得到了这个工作。 My big issue was my json was a string (in my database...and not a specific/known Type).我的大问题是我的 json 是一个字符串(在我的数据库中......而不是特定/已知类型)。

Ok, I finally got this to work.好的,我终于得到了这个工作。

////[Route("api/[controller]")]
////[ApiController]
////public class MyController: Microsoft.AspNetCore.Mvc.ControllerBase
////{
                    //// public IActionResult MyMethod(string myParam) {

                    string hardCodedJson = "{}";
                    int hardCodedStatusCode = 200;

                    Newtonsoft.Json.Linq.JObject job = Newtonsoft.Json.Linq.JObject.Parse(hardCodedJson);
                    /* "this" comes from your class being a subclass of Microsoft.AspNetCore.Mvc.ControllerBase */
                    Microsoft.AspNetCore.Mvc.ContentResult contRes = this.Content(job.ToString());
                    contRes.StatusCode = hardCodedStatusCode;

                    return contRes;

                    //// } ////end MyMethod
              //// } ////end class

I happen to be on asp.net core 3.1我碰巧在asp.net core 3.1上

#region Assembly Microsoft.AspNetCore.Mvc.Core, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
//C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\netcoreapp3.1\Microsoft.AspNetCore.Mvc.Core.dll

I got the hint from here :: https://www.jianshu.com/p/7b3e92c42b61我从这里得到了提示 :: https://www.jianshu.com/p/7b3e92c42b61

The cleanest solution I have found is to set the following in my ConfigureServices method in Startup.cs (In my case I want the TZ info stripped. I always want to see the date time as the user saw it).我发现的最干净的解决方案是在 Startup.cs 的 ConfigureServices 方法中设置以下内容(在我的情况下,我希望 TZ 信息被剥离。我总是希望看到用户看到的日期时间)。

   services.AddControllers()
                .AddNewtonsoftJson(o =>
                {
                    o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
                });

The DateTimeZoneHandling options are Utc, Unspecified, Local or RoundtripKind DateTimeZoneHandling 选项是 Utc、Unspecified、Local 或 RoundtripKind

I would still like to find a way to be able to request this on a per-call bases.我仍然想找到一种方法可以在每次调用的基础上请求这个。

something like就像是

  static readonly JsonMediaTypeFormatter _jsonFormatter = new JsonMediaTypeFormatter();
 _jsonFormatter.SerializerSettings = new JsonSerializerSettings()
                {DateTimeZoneHandling = DateTimeZoneHandling.Unspecified};

return Ok("Hello World", _jsonFormatter );

I am converting from ASP.NET and there I used the following helper method我正在从 ASP.NET 转换,在那里我使用了以下帮助方法

public static ActionResult<T> Ok<T>(T result, HttpContext context)
    {
        var responseMessage = context.GetHttpRequestMessage().CreateResponse(HttpStatusCode.OK, result, _jsonFormatter);
        return new ResponseMessageResult(responseMessage);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM