【问题标题】:ASP.NET Core return JSON with status codeASP.NET Core 返回带有状态码的 JSON
【发布时间】:2017-07-10 15:38:37
【问题描述】:

我正在寻找在我的 .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"));
}

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

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

【问题讨论】:

  • “ok”方法返回 200 作为状态码。预定义的方法涵盖了所有常见情况。要返回 201(带有新资源位置的 + 标头),请使用 CreatedAtRoute 方法等。

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


【解决方案1】:

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

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

DateTimeZoneHandling 选项为 Utc、未指定、本地或 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);
    }

【讨论】:

    【解决方案2】:

    ASP.NET Core Web API 中的控制器操作返回类型 02/03/2020

    6 分钟阅读 +2

    斯科特·艾迪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);
    }
    

    【讨论】:

      【解决方案3】:

      我得到了这个工作。我的大问题是我的 json 是一个字符串(在我的数据库中......而不是特定/已知类型)。

      好吧,我终于搞定了。

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

      【讨论】:

        【解决方案4】:

        使用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

        如果需要发送特定状态码,请改用ObjectResultStatusCode。两者都做同样的事情,并支持内容协商。

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

        甚至更细粒度的 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 };
        

        如果您特别想以 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 来做同样的事情

        the official docs 中了解有关它们的更多信息。了解filters here

        示例中使用的简单模型类

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

        【讨论】:

        • 这是一个很好的答案,因为它着重于问题并简要解释了一些实用性。
        • 如何使用Post方式路由兄弟?
        • 我的意思是这个 sn-p : [HttpPost("AsJson")] 我的兄弟,谢谢
        • “硬编码 json”示例对我不起作用。它将字符串解析为 JSON 并返回带有双引号 (") 的字符串,其中 json 字符已转义。因此,我使用 ContentResult 而不是 ObjectResult,如下所示:return new ContentResult() { Content = hardCodedJson, StatusCode = ( int)HttpStatusCode.OK, ContentType = "application/json" };
        【解决方案5】:

        您为最常见的状态代码预定义了方法。

        • Ok(result) 返回 200 并响应
        • CreatedAtRoute 返回 201 + 新资源 URL
        • NotFound 返回404
        • BadRequest 返回400 等。

        有关所有方法的列表,请参阅 BaseController.csController.cs

        但如果你真的坚持可以使用StatusCode 来设置自定义代码,但你真的不应该这样做,因为它会降低代码的可读性并且你必须重复代码来设置标题(比如CreatedAtRoute) .

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

        【讨论】:

        • 这让我了解了我在下面的回复。谢谢
        • 此代码不适用于 ASP.NET Core 2.2。我刚刚尝试过,它序列化为JSON ActionResultJson() 方法创建。它不直接包含“123”字符串。
        • @amedina:我的错,只需删除 Json(...) 并将字符串传递给 StatusCode
        • 当你说“Ok(result)”时——结果是什么?它是 JSON 格式字符串还是 C# 对象(自动转换为 JSON 字符串?)?
        • @variable:始终是 POCO/类/对象。如果你想返回一个字符串,你需要使用“内容”来代替
        【解决方案6】:

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

        var result = new Item { Id = 123, Name = "Hero" };
        
        return new JsonResult(result)
        {
            StatusCode = StatusCodes.Status201Created // Status code here 
        };
        

        【讨论】:

        • 我认为这比@t​​seng 的回答要好,因为他的解决方案包括状态代码等的重复字段。
        • 您可以进行的一项改进是使用 Microsoft.AspNetCore.Http 中定义的 StatusCodes,如下所示: return new JsonResult(new { }) { StatusCode = StatusCodes.Status404NotFound };
        • 这应该是公认的答案。尽管有很多方法可以通用地设置 json,但有时我们必须使用旧端点,并且设置可能会有所不同。在我们可以停止支持某些遗留端点之前,这是拥有完全控制权的终极方式
        • Microsoft.AspNetCore.Mvc.JsonResult 是我认为的完全限定名称。没有 FQN 或“使用”答案让我发疯。 :) 程序集 Microsoft.AspNetCore.Mvc.Core,版本=3.1.0.0,文化=中性,PublicKeyToken=adb9793829ddae60 // C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\ netcoreapp3.1\Microsoft.AspNetCore.Mvc.Core.dll
        • 当我有一个强类型时,这对我有用(在这个例子中是“ITem result = new Item”......Item 是运行时的已知类型))。请参阅我的回答(对这个问题),了解该类型何时 ~ 不 ~ 已知。 (我在 db 中有 json .. 并且 json 类型在运行时未知)。谢谢杰拉德。
        【解决方案7】:

        我在这里找到了很棒的答案,我还尝试了这个返回语句,参见StatusCode(whatever code you wish),它奏效了!!!

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

        【讨论】:

        • 喜欢这个!好建议!
        【解决方案8】:

        我在我的 Asp Net Core Api 应用程序中所做的是创建一个从 ObjectResult 扩展的类,并提供许多构造函数来自定义内容和状态代码。 然后我的所有控制器操作都使用其中一个构造函数作为适当的。 您可以在以下位置查看我的实现: 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),你应该很高兴。

        【讨论】:

          【解决方案9】:

          使用枚举而不是使用 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 !!!"));
              }
          

          【讨论】:

          • 枚举是个好主意!
          【解决方案10】:

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

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

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

          【讨论】:

            【解决方案11】:

            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 进行更精细的控制,而不是仅仅使用 VS 中“新项目”模板中的默认配置。

            让我们确保您掌握了一些基础知识...

            第 1 步:配置您的服务

            为了让您的 ASP.NET Core WebAPI 响应 JSON 序列化对象以及对状态代码的完全控制,您应该首先确保在您的 ConfigureServices 方法中包含 AddMvc() 服务通常在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() 方法的副本。但是,我们通过定义每个服务而不是使用带有模板的预发布服务来自行实现每个“默认”服务。我已经在代码块中添加了仓库链接,或者你可以查看AddMvc()from the GitHub repository.

            请注意,有一些指南会尝试通过“撤消”默认设置来解决这个问题,而不是一开始就不实施它...如果您考虑到这一点,我们现在正在使用 Open来源,这是多余的工作,糟糕的代码,坦率地说,这是一个很快就会消失的旧习惯。


            第 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
            

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

            更多信息可以在这个.NET Web Development and Tools Blog entry找到

            【讨论】:

            • 非常感谢您所付出的努力。您的回答激励我使用IActionResult 实现return Ok(new {response = "123"}); 干杯!
            • @Rossco 没问题。希望其余代码将在您的项目开发过程中为您提供指导。
            • 为了扩展这个主题,我在这里创建了一个额外且更完整的实现 WebAPI 指南:stackoverflow.com/q/42365275/3645638
            • 开启设置:RespectBrowserAcceptHeader = true;您没有解释为什么要这样做,而且这样做通常是不必要和错误的。浏览器要求 html,因此无论如何它们都不应该影响格式化程序的选择(不幸的是,chrome 是通过要求 XML 来实现的)。简而言之,我不会这样做,您指定的后备已经是默认行为
            • @YishaiGalatzer 我回答的那部分的主题是强调如何减轻客户端和 API 逻辑之间的默认中间件的负担。在我看来,RespectBrowserAcceptHeader 在实现使用替代序列化程序或更常见的情况下至关重要,当您想确保您的客户端不会发送格式错误的请求时。因此,我强调“如果您的项目需要完全控制并且您想严格定义您的服务”,并注意该语句上方突出显示的块引用。
            【解决方案12】:

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

            【讨论】:

            • 没有。这很糟糕。
            猜你喜欢
            • 1970-01-01
            • 2012-07-07
            • 1970-01-01
            • 2019-12-01
            • 2020-11-10
            • 2018-11-28
            • 1970-01-01
            • 2023-02-25
            相关资源
            最近更新 更多