ASP.NET Core trả về JSON với mã trạng thái


153

Tôi đang tìm cách chính xác để trả về JSON bằng mã trạng thái HTTP trong bộ điều khiển API Web .NET của tôi. Tôi sử dụng để sử dụng nó như thế này:

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

Đây là một ứng dụng MVC 4.6 nhưng bây giờ với .NET Core tôi dường như không có cái này IHttpActionResulttôi có ActionResultvà sử dụng như thế này:

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

Nhưng phản hồi từ máy chủ thật kỳ lạ, như trong hình dưới đây:

nhập mô tả hình ảnh ở đây

Tôi chỉ muốn bộ điều khiển API Web trả về JSON bằng mã trạng thái HTTP như tôi đã làm trong Web API 2.


1
Các phương thức "ok" trả về 200 dưới dạng mã trạng thái. Các phương pháp được xác định trước bao gồm tất cả các trường hợp phổ biến. Để trở về 201 (+ tiêu đề với vị trí tài nguyên mới), bạn sử dụng CreatedAtRoutephương thức, v.v.
Tseng

Câu trả lời:


191

Phiên bản cơ bản nhất đáp ứng với a JsonResultlà:

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

Tuy nhiên, điều này sẽ không giúp ích cho vấn đề của bạn vì bạn không thể giải quyết rõ ràng với mã phản hồi của chính mình.

Cách để có quyền kiểm soát kết quả trạng thái, là bạn cần trả về một ActionResultnơi mà sau đó bạn có thể tận dụng lợi thế của StatusCodeResultloại.

ví dụ:

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

Lưu ý cả hai ví dụ trên đến từ một hướng dẫn tuyệt vời có sẵn từ Tài liệu của Microsoft: Định dạng dữ liệu phản hồi


Thêm thứ

Vấn đề tôi gặp phải khá thường xuyên là tôi muốn có nhiều quyền kiểm soát chi tiết hơn đối với WebAPI của mình thay vì chỉ đi với cấu hình mặc định từ mẫu "Dự án mới" trong VS.

Hãy chắc chắn rằng bạn có một số điều cơ bản ...

Bước 1: Định cấu hình Dịch vụ của bạn

Để làm cho ASP.NET Core WebAPI của bạn phản hồi với Đối tượng được tuần tự hóa JSON cùng với toàn quyền kiểm soát mã trạng thái, bạn nên bắt đầu bằng cách đảm bảo rằng bạn đã bao gồm AddMvc()dịch vụ trong ConfigureServicesphương thức của bạn thường được tìm thấy Startup.cs.

Điều quan trọng cần lưu ý là AddMvc()sẽ tự động bao gồm Trình định dạng đầu vào / đầu ra cho JSON cùng với việc trả lời các loại yêu cầu khác.

Nếu dự án của bạn yêu cầu toàn quyền kiểm soát và bạn muốn xác định nghiêm ngặt các dịch vụ của mình, chẳng hạn như cách WebAPI của bạn sẽ hoạt động với các loại yêu cầu khác nhau bao gồm application/jsonvà không đáp ứng với các loại yêu cầu khác (như yêu cầu trình duyệt tiêu chuẩn), bạn có thể xác định thủ công với mã sau:

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

Bạn sẽ nhận thấy rằng tôi cũng đã bao gồm một cách để bạn thêm các trình định dạng Đầu vào / Đầu ra tùy chỉnh của riêng bạn, trong trường hợp bạn có thể muốn phản hồi với định dạng tuần tự hóa khác (protobuf, tiết kiệm, v.v.).

Đoạn mã ở trên hầu hết là một bản sao của AddMvc()phương thức. Tuy nhiên, chúng tôi tự thực hiện từng dịch vụ "mặc định" bằng cách xác định từng dịch vụ thay vì đi với dịch vụ được vận chuyển trước với mẫu. Tôi đã thêm liên kết kho lưu trữ trong khối mã hoặc bạn có thể kiểm tra AddMvc() từ kho GitHub. .

Lưu ý rằng có một số hướng dẫn sẽ cố gắng giải quyết vấn đề này bằng cách "hoàn tác" các mặc định, thay vì không triển khai nó ngay từ đầu ... Nếu bạn cho rằng chúng tôi hiện đang làm việc với Nguồn mở, thì đây là công việc dư thừa , mã xấu và thẳng thắn một thói quen cũ sẽ sớm biến mất.


Bước 2: Tạo Trình điều khiển

Tôi sẽ cho bạn thấy một câu hỏi thực sự đơn giản chỉ để sắp xếp câu hỏi của bạn.

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

Bước 3: Kiểm tra Content-TypeAccept

Bạn cần đảm bảo rằng các tiêu đề Content-Typeyêu cầuAccept của bạn được đặt đúng. Trong trường hợp của bạn (JSON), bạn sẽ muốn thiết lập nó thành .application/json

Nếu bạn muốn WebAPI của bạn trả lời dưới dạng JSON như mặc định, bất kể tiêu đề yêu cầu đang chỉ định bạn có thể làm điều đó theo một vài cách .

Cách 1 Như được hiển thị trong bài viết tôi đã đề xuất trước đó ( Định dạng dữ liệu phản hồi ), bạn có thể buộc một định dạng cụ thể ở cấp Bộ điều khiển / Hành động. Cá nhân tôi không thích cách tiếp cận này ... nhưng ở đây nó là hoàn chỉnh:

Buộc một định dạng cụ thể Nếu bạn muốn hạn chế các định dạng phản hồi cho một hành động cụ thể bạn có thể, bạn có thể áp dụng bộ lọc [Sản xuất]. Bộ lọc [Sản xuất] chỉ định các định dạng phản hồi cho một hành động cụ thể (hoặc bộ điều khiển). Giống như hầu hết các Bộ lọc, điều này có thể được áp dụng ở hành động, bộ điều khiển hoặc phạm vi toàn cầu.

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

Bộ [Produces]lọc sẽ buộc tất cả các hành động trong các AuthorsControllerphản hồi được định dạng JSON, ngay cả khi các trình định dạng khác được định cấu hình cho ứng dụng và máy khách cung cấp một Accepttiêu đề yêu cầu một định dạng khác, có sẵn.

Cách 2 Phương pháp ưa thích của tôi là để WebAPI đáp ứng tất cả các yêu cầu với định dạng được yêu cầu. Tuy nhiên, trong trường hợp nó không chấp nhận định dạng được yêu cầu, thì hãy quay lại mặc định (ví dụ: JSON)

Trước tiên, bạn sẽ cần phải đăng ký trong tùy chọn của mình (chúng tôi cần làm lại hành vi mặc định, như đã lưu ý trước đó)

options.RespectBrowserAcceptHeader = true; // false by default

Cuối cùng, bằng cách đơn giản sắp xếp lại danh sách các trình định dạng được xác định trong trình xây dựng dịch vụ, máy chủ web sẽ mặc định cho trình định dạng bạn định vị ở đầu danh sách (ví dụ vị trí 0).

Thông tin thêm có thể được tìm thấy trong mục Công cụ và Phát triển Web .NET này


Thanx rất nhiều cho những nỗ lực bạn bỏ ra. Câu trả lời của bạn đã truyền cảm hứng cho tôi thực hiện IActionResultvới return Ok(new {response = "123"});Cheers!
Rossco

1
@Rossco Không có vấn đề. Hy vọng phần còn lại của mã sẽ giúp hướng dẫn bạn khi dự án của bạn phát triển.
Svek

1
Để mở rộng chủ đề này, tôi đã tạo một hướng dẫn bổ sung và đầy đủ hơn để triển khai WebAPI tại đây: stackoverflow.com/q/42365275/3645638
Svek

Khi cài đặt: respectBrowserAcceptHeader = true; Bạn không giải thích lý do tại sao bạn làm việc đó và thường không cần thiết và sai khi làm như vậy. Các trình duyệt yêu cầu html và do đó chúng không nên ảnh hưởng đến lựa chọn định dạng trong bất kỳ cách nào (chrome không may làm điều đó bằng cách yêu cầu XML). Nói tóm lại, đó là điều tôi sẽ giữ lại và dự phòng mà bạn đang chỉ định đã là hành vi mặc định
Yishai Galatzer

@YishaiGalatzer Chủ đề chính của phần đó trong câu trả lời của tôi là làm nổi bật cách giải nén phần mềm trung gian mặc định giữa máy khách và logic API. Theo tôi, RespectBrowserAcceptHeaderrất quan trọng khi triển khai sử dụng bộ nối tiếp thay thế hoặc phổ biến hơn, khi bạn muốn đảm bảo rằng khách hàng của bạn không gửi yêu cầu không đúng định dạng. Do đó, tôi nhấn mạnh "Nếu dự án của bạn yêu cầu toàn quyền kiểm soát và bạn muốn xác định nghiêm ngặt dịch vụ của mình" và lưu ý trích dẫn khối được tô sáng bên trên tuyên bố đó.
Svek

57

Bạn có các phương thức được xác định trước cho hầu hết các mã trạng thái phổ biến.

  • Ok(result)trả 200lời với phản hồi
  • CreatedAtRoutetrả về 201+ URL tài nguyên mới
  • NotFound trả lại 404
  • BadRequesttrả lại 400vv

Xem BaseController.csController.cscho một danh sách của tất cả các phương pháp.

Nhưng nếu bạn thực sự khăng khăng bạn có thể sử dụng StatusCodeđể đặt mã tùy chỉnh, nhưng bạn thực sự không nên vì nó làm cho mã ít đọc hơn và bạn sẽ phải lặp lại mã để đặt tiêu đề (như cho CreatedAtRoute).

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

1
điều này đã cho tôi cái nhìn sâu sắc để trả lời của tôi dưới đây. Cảm ơn bạn
Oge Nwike

Mã này không đúng với ASP.NET Core 2.2. Tôi chỉ đã thử nó và nó serializes vào JSONcác ActionResulttạo ra bởi các Json()phương pháp. Nó không bao gồm chuỗi "123" trực tiếp.
Amedina

1
@amedina: Thật tệ, chỉ cần gỡ bỏ Json(...)và chuyển chuỗi đến StatusCode
Tseng

Khi bạn nói "Ok (kết quả)" - kết quả là gì? Đây có phải là một chuỗi định dạng JSON hay nó là một đối tượng C # (tự động được chuyển đổi thành chuỗi JSON?)?
biến

@variable: Luôn là POCO / class / object. Nếu bạn muốn trả về một chuỗi, bạn cần sử dụng "Nội dung" thay thế
Tseng

42

Với ASP.NET Core 2.0 , cách lý tưởng để trả về đối tượng Web API(được hợp nhất với MVC và sử dụng cùng một lớp cơ sở Controller) là

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

Thông báo rằng

  1. Nó trả về với 200 OKmã trạng thái (nó là một Okloại ObjectResult)
  2. Nó thực hiện đàm phán nội dung, tức là nó sẽ trở lại dựa trên Accepttiêu đề trong yêu cầu. Nếu Accept: application/xmlđược gửi trong yêu cầu, nó sẽ trở lại như XML. Nếu không có gì được gửi, JSONlà mặc định.

Nếu nó cần gửi với mã trạng thái cụ thể , sử dụng ObjectResulthoặc StatusCodethay vào đó. Cả hai đều làm điều tương tự, và hỗ trợ đàm phán nội dung.

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

hoặc thậm chí tốt hơn với 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 };

Nếu bạn đặc biệt muốn trở lại dưới dạng JSON , có một số cách

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

Thông báo rằng

  1. Cả hai thực thi JSONtheo hai cách khác nhau.
  2. Cả hai đều bỏ qua đàm phán nội dung.
  3. Phương thức đầu tiên thực thi JSON với trình tuần tự hóa cụ thể Json(object) .
  4. Phương thức thứ hai thực hiện tương tự bằng cách sử dụng Produces()thuộc tính (là mộtResultFilter ) vớicontentType = application/json

Đọc thêm về họ trong các tài liệu chính thức . Tìm hiểu về các bộ lọc ở đây .

Lớp mô hình đơn giản được sử dụng trong các mẫu

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

10
Đây là một câu trả lời tốt vì nó tập trung vào câu hỏi và giải thích một số thực tế ngắn gọn.
netfed

33

Cách dễ nhất tôi nghĩ ra là:

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

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

2
Tôi nghĩ rằng điều này tốt hơn câu trả lời từ @tseng vì giải pháp của anh ấy bao gồm các trường trùng lặp cho Mã trạng thái, v.v.
Christian Sauer

2
Một cải tiến bạn có thể thực hiện là sử dụng StatusCodes được xác định trong Microsoft.AspNetCore.Http như thế này: trả về JsonResult mới (new {}) {StatusCode = StatusCodes.Status404NotFound};
Bryan Bedard

2
Đây phải là câu trả lời được chấp nhận. Mặc dù có nhiều cách để thiết lập json, đôi khi chúng ta phải làm việc với các điểm cuối kế thừa và các cài đặt có thể khác nhau. Cho đến khi chúng tôi có thể ngừng hỗ trợ một số điểm cuối kế thừa, đây là cách cuối cùng để có toàn quyền kiểm soát
pqsk

Microsoft.AspNetCore.Mvc.JsonResult là tên đủ điều kiện tôi nghĩ. Không có câu trả lời FQN hoặc "sử dụng" nào thúc đẩy tôi. :) Hội Microsoft.AspNetCore.Mvc.Core, Phiên bản = 3.1.0.0, Culture = trung tính, PublicKeyToken = adb9793829ddae60 // C: \ Chương trình tập tin \ dotnet \ pack \ Microsoft.AspNetCore.App.Ref \ 3.1.0 \ ref \ netcorzon3.1 \ Microsoft.AspNetCore.Mvc.Core.dll
granadaCoder

1
Điều này làm việc cho tôi khi tôi có một loại mạnh ("ITem result = new Item" trong ví dụ này ... Item là một loại đã biết trong thời gian chạy)). Xem câu trả lời của tôi (cho câu hỏi này) khi loại ~ không được biết đến. (Tôi đã có json trong một db..và loại json không được biết đến trong thời gian chạy). Cảm ơn Gerald.
granadaCoder

15

Đây là giải pháp đơn giản nhất của tôi:

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

hoặc là

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

4

Thay vì sử dụng mã trạng thái 404/201 bằng enum

     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 !!!"));
    }

Enum là một ý tưởng tuyệt vời!
Bhimbim

2

Câu trả lời tuyệt vời tôi tìm thấy ở đây và tôi cũng đã thử tuyên bố trở lại này xem StatusCode(whatever code you wish)và nó đã hoạt động !!!

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

1
Giống như cái này! Gợi ý tốt!
cù lét

0

Vui lòng tham khảo mã bên dưới, Bạn có thể quản lý nhiều mã trạng thái với các loại JSON khác nhau

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.");
    }
}

9
Không. Điều này thật tệ.
Phillip Copley

0

Những gì tôi làm trong các ứng dụng Asp Net Core Api của mình là tạo một lớp mở rộng từ ObjectResult và cung cấp nhiều hàm tạo để tùy chỉnh nội dung và mã trạng thái. Sau đó, tất cả các hành động Trình điều khiển của tôi sử dụng một trong các trình xây dựng để chiếm đoạt. Bạn có thể xem triển khai của tôi tại: https://github.com/melardev/AspNetCoreApiPagratedCrud

https://github.com/melardev/ApiAspCoreEc Commerce

Đây là cách lớp học trông như thế nào (đi tới repo của tôi để lấy mã đầy đủ):

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

Lưu ý cơ sở (dto) bạn thay thế dto bằng đối tượng của bạn và bạn nên đi.


0

Tôi đã làm điều này để làm việc. Vấn đề lớn của tôi là json của tôi là một chuỗi (trong cơ sở dữ liệu của tôi ... và không phải là Loại cụ thể / đã biết).

Ok, cuối cùng tôi đã có được điều này để làm việc.

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

Tôi tình cờ ở trên 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

Tôi đã nhận được gợi ý từ đây :: https://www.jianshu.com/p/7b3e92c42b61

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.