首页 文章

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

提问于
浏览
107

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

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

这是在一个 4.6 MVC 应用程序,但现在有.NET 核心我似乎没有这个IHttpActionResult我有ActionResult并使用这样:

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

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

在此输入图像描述

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

9 回答

  • 139

    使用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 文档中提供的精彩指南:格式化响应数据


    额外的东西

    我经常遇到的问题是我希望对我的 WebAPI 进行更细粒度的控制,而不是仅使用 VS 中“新建项目”模板的默认配置。

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

    第 1 步:配置您的服务

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

    重要的是要注意AddMvc()将自动包含 Input/Output Formatter for 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)
    }
    

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

    上面的代码块大部分是AddMvc()方法的副本。但是,我们通过定义每个服务而不是使用带有模板的 pre-shipped 来实现每个“默认”服务。我在代码块中添加了存储库链接,或者您可以查看AddMvc() 来自 GitHub 存储库。

    请注意,有一些指南会试图通过“撤消”默认值来解决这个问题,而不是仅仅是首先实现它...如果你考虑到我们现在正在使用开源,这是多余的工作,糟糕的代码和坦率的旧习惯很快就会消失。


    第 2 步:创建一个控制器

    我要给你一个真正的 straight-forward 只是为了让你的问题排序。

    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-Type 并接受

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

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

    方式 1如我之前建议的文章(格式化响应数据)所示,您可以在 Controller/Action 级别强制使用特定格式。我个人不喜欢这种方法......但这里是为了完整性:

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

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

    即使为应用程序配置了其他格式化程序,并且客户端提供了一个请求不同可用格式的Accept标头,[Produces]过滤器也会强制AuthorsController中的所有操作返回 JSON-formatted 响应。

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

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

    options.RespectBrowserAcceptHeader = true; // false by default
    

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

    更多信息可在此.NET Web 开发和工具博客条目找到

  • 45

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

    • Ok(result)返回带响应的200

    • CreatedAtRoute返回201新资源 URL

    • NotFound返回404

    • BadRequest返回400

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

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

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

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

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

    请注意

    • 它返回200 OK状态代码(它是ObjectResult类型的ObjectResult)

    • 它进行内容协商,i.e。它将根据请求中的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" });
    

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

    请注意

    • 两者都以两种不同的方式强制执行JSON

    • 两者都忽略了内容协商。

    • 第一种方法使用特定的序列化程序Json(object)强制执行 JSON。

    • 第二种方法使用contentType = application/json属性(即ResultFilter)与contentType = application/json相同

    官方文件中阅读有关它们的更多信息。了解过滤器

    样本中使用的简单模型类

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

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

    var result = new Item { Id = 123, Name = "Hero" };
    
    return new JsonResult(result)
    {
        StatusCode = StatusCodes.Status201Created // Status code here 
    };
    
  • 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"});
    }
    
  • 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 !!!"));
        }
    
  • 0

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

    https://github.com/melardev/ApiAspCoreEcommerce

    这里是类看起来 like(go 到我的 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;
        }
    }
    

    注意 base(dto)你用你的对象替换 dto,你应该好好去。

  • 0

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

    return Ok(new {
                        Token = new JwtSecurityTokenHandler().WriteToken(token),
                        Expiration = token.ValidTo,
                        username = user.FullName,
                        StatusCode = StatusCode(200)
                    });
    
  • -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.");
        }
    }
    

相关问题