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

时间:2017-02-21 06:26:48

标签: c# json asp.net-core asp.net-core-webapi

我正在寻找在我的.NET Core Web API控制器中使用HTTP状态代码返回JSON的正确方法。我习惯这样使用它:

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

这是在一个4.6 MVC的应用程序中但现在使用.NET Core我似乎没有IHttpActionResult我有ActionResult并使用如下:

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

但服务器的响应很奇怪,如下图所示:

enter image description here

我只是希望Web API控制器返回带有HTTP状态代码的JSON,就像我在Web API 2中所做的那样。

12 个答案:

答案 0 :(得分:132)

使用JsonResult回复的最基本版本是:

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

但是,这不能帮助解决您的问题,因为您无法明确处理自己的响应代码。

  

控制状态结果的方法是,您需要返回ActionResult,然后您可以使用StatusCodeResult类型。

例如:

// 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);
}

请注意,上述两个示例均来自Microsoft文档中提供的精彩指南:Formatting Response Data

额外的东西

我经常遇到的问题是我希望对我的WebAPI进行更精细的控制,而不是仅仅使用" New Project"中的默认配置。 VS中的模板。

让我们确保你有一些基础知识......

第1步:配置您的服务

为了让您的ASP.NET Core WebAPI能够在完全控制状态代码的情况下使用JSON序列化对象进行响应,您应该首先确保已在{{{AddMvc()中包含ConfigureServices服务。通常在Startup.cs中找到的方法。

  

值得注意的是,AddMvc()会自动包含JSON的输入/输出格式化程序以及响应其他请求类型。

如果您的项目需要完全控制,并且您希望严格定义服务,例如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)
}

您会注意到我还提供了一种方法来添加您自己的自定义输入/输出格式化程序,如果您可能想要响应另一种序列化格式(protobuf,thrift等)。

上面的代码块大部分是AddMvc()方法的副本。但是,我们正在实施每个&#34;默认&#34;通过定义每个服务而不是使用模板预先发送的服务来自行服务。我在代码块中添加了存储库链接,或者您可以查看AddMvc() from the GitHub repository.

请注意,有些指南会尝试通过&#34;撤消&#34;来解决此问题。默认情况,而不仅仅是首先没有实现它...如果你考虑到我们现在正在使用开源,这是多余的工作,糟糕的代码和坦率的旧习惯很快就会消失。 / em>的

第2步:创建控制器

我会告诉你一个非常简单的问题,只是为了让你的问题得到分类。

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();
    }
}

第3步:检查您的Content-TypeAccept

您需要确保请求中的Content-TypeAccept标头设置正确。在您的情况下(JSON),您需要将其设置为application/json

如果您希望WebAPI作为默认值响应JSON,则无论请求标头指定了什么,您都可以成对方式执行此操作。

方式1 如我之前推荐的文章(Formatting Response Data)所示,您可以在Controller / Action级别强制使用特定格式。我个人不喜欢这种方法......但这是为了完整性:

  

强制使用特定格式如果您想限制特定操作的响应格式,可以应用   [生产]过滤器。 [Produces]过滤器指定响应   特定操作(或控制器)的格式。像大多数过滤器一样   可以应用于动作,控制器或全局范围。

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

[Produces]过滤器会强制执行所有操作   AuthorsController返回JSON格式的响应,即使是其他响应   格式化程序是为应用程序和客户端提供的   Accept标头请求不同的可用格式。

方式2 我首选的方法是让WebAPI以所请求的格式响应所有请求。但是,如果它不接受所请求的格式,则后退为默认值(即JSON)

首先,您需要在选项中注册(我们需要重做默认行为,如前所述)

options.RespectBrowserAcceptHeader = true; // false by default

最后,通过简单地重新排序服务构建器中定义的格式化程序列表,Web主机将默认为您位于列表顶部的格式化程序(即位置0)。

可在此.NET Web Development and Tools Blog entry

中找到更多信息

答案 1 :(得分:42)

您有针对大多数常见状态代码的预定义方法。

  • Ok(result)返回带有回复的200
  • CreatedAtRoute返回201 +新资源网址
  • NotFound返回404
  • BadRequest返回400

请参阅BaseController.csController.cs以获取所有方法的列表。

但是,如果您真的坚持要求{(1}}}设置自定义代码,但您确实不应该因为它使代码的可读性降低而且您不得不重复代码来设置标题(喜欢StatusCode)。

CreatedAtRoute

答案 2 :(得分:26)

使用 ASP.NET Core 2.0 ,从Web API(与MVC统一并使用相同的基类Controller)返回对象的理想方法是

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

请注意

  1. 返回200 OK状态代码(Ok类型ObjectResult
  2. 它进行内容协商,即它会根据请求中的Accept标头返回。如果在请求中发送了Accept: application/xml,它将以XML的形式返回。如果未发送任何内容,则JSON为默认值。
  3. 如果需要发送带有特定状态代码的 ,请改用ObjectResultStatusCode。两者都做同样的事情,并支持内容协商。

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

    如果你特意想以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" };
    }
    

    请注意

    1. 两者都以两种不同的方式强制执行JSON
    2. 两者都忽略了内容协商。
    3. 第一种方法使用特定序列化程序Json(object)强制执行JSON。
    4. 使用Produces()属性(ResultFilter)和contentType = application/json
    5. ,第二种方法也是如此

      the official docs中详细了解它们。了解filters here

      样本中使用的简单模型类

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

答案 3 :(得分:15)

我想出的最简单的方法是:

return new JsonResult(result)
{
    StatusCode = 201 // Status code here 
};

答案 4 :(得分:7)

这是我最简单的解决方案:

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

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

答案 5 :(得分:3)

代替使用枚举使用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 !!!"));
    }

答案 6 :(得分:1)

ASP.NET Core Web API中的控制器操作返回类型 2020年2月3日

6分钟阅读 +2

由Scott Addie 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);
}

答案 7 :(得分:0)

我在Asp Net Core Api应用程序中要做的是创建一个从ObjectResult扩展的类,并提供许多构造函数来自定义内容和状态代码。 然后,我所有的Controller动作都会适当地使用其中一个构造函数。 您可以在以下位置查看我的实现: https://github.com/melardev/AspNetCoreApiPaginatedCrud

https://github.com/melardev/ApiAspCoreEcommerce

这是类的外观(转到我的仓库以获取完整代码):

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;
    }
}

请注意,用对象替换dto的base(dto),您应该一切顺利。

答案 8 :(得分:0)

我在这里找到了不错的答案,我也尝试了这个return语句,请参见StatusCode(whatever code you wish),并且有效!!

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

答案 9 :(得分:0)

我让它起作用。我的大问题是我的json是一个字符串(在我的数据库中……而不是特定的/已知的Type)。

好吧,我终于明白了。

////[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

我恰好位于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

我从这里得到了提示:: https://www.jianshu.com/p/7b3e92c42b61

答案 10 :(得分:0)

我找到的最干净的解决方案是在 Startup.cs 的 ConfigureServices 方法中设置以下内容(在我的情况下,我希望删除 TZ 信息。我总是希望看到用户看到的日期时间)。

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

DateTimeZoneHandling 选项为 Utc、Unspecified、Local 或 RoundtripKind

我仍然希望找到一种方法,能够在每次调用的基础上提出请求。

类似的东西

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

return Ok("Hello World", _jsonFormatter );

我正在从 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);
    }

答案 11 :(得分:-1)

请参考以下代码,您可以使用不同类型的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.");
    }
}