Programing

상태 코드가있는 ASP.NET Core 반환 JSON

crosscheck 2020. 7. 23. 08:10
반응형

상태 코드가있는 ASP.NET Core 반환 JSON


.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 컨트롤러가 Web API 2에서했던 것처럼 HTTP 상태 코드로 JSON을 반환하기를 원합니다.


a JsonResult응답하는 가장 기본적인 버전 은 다음 같습니다.

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

그러나 자신의 응답 코드를 명시 적으로 처리 할 수 ​​없기 때문에 문제를 해결하는 데 도움이되지 않습니다.

상태 결과를 제어하는 ​​방법 ActionResultStatusCodeResult유형 을 활용할 수있는 곳 을 반환해야한다는 것입니다 .

예를 들면 다음과 같습니다.

// 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 설명서에서 제공하는 훌륭한 가이드에서 제공 한 것입니다. 응답 데이터 형식화


추가 물건

내가 자주 접하는 문제는 VS의 "New Project"템플릿에서 기본 구성을 사용하는 대신 WebAPI를보다 세밀하게 제어하기를 원한다는 것입니다.

몇 가지 기본 사항이 있는지 확인하십시오 ...

1 단계 : 서비스 구성

ASP.NET Core WebAPI가 상태 코드를 완전히 제어하면서 JSON Serialized Object로 응답하도록하려면 먼저에서 찾은 방법에 AddMvc()서비스를 포함시켜야합니다 .ConfigureServicesStartup.cs

AddMvc()다른 요청 유형에 대한 응답과 함께 JSON 용 입력 / 출력 포맷터가 자동으로 포함 된다는 점에 유의해야 합니다.

프로젝트에 모든 권한이 필요 application/json하고 다른 요청 유형 (예 : 표준 브라우저 요청)을 포함하여 응답하지 않는 다양한 요청 유형에 대해 WebAPI가 작동하는 방식과 같이 서비스를 엄격하게 정의하려는 경우 다음을 사용하여 수동으로 정의 할 수 있습니다. 다음 코드 :

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() GitHub 리포지토리에서 확인할 수 있습니다 . .

처음에는 기본값을 구현하지 않고 기본값을 "취소"하여이 문제를 해결하려는 몇 가지 안내서가 있습니다. 현재 오픈 소스로 작업하고 있다고 생각하면 중복 작업입니다. , 나쁜 코드와 솔직히 오래 사라질 습관.


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 이전에 권장 한 기사 ( 응답 데이터 형식화 )에 표시된 것처럼 컨트롤러 / 액션 수준에서 특정 형식을 강제 할 수 있습니다. 나는 개인적 으로이 접근법을 좋아하지 않지만 ... 완전성을위한 것입니다.

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. The [Produces] filter specifies the response formats for a specific action (or controller). 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.

Way 2 My preferred method is for the WebAPI to respond to all requests with the format requested. However, in the event that it doesn't accept the requested format, then fall-back to a default (ie. JSON)

먼저 옵션에 등록해야합니다 (앞서 언급 한대로 기본 동작을 다시 작업해야 함).

options.RespectBrowserAcceptHeader = true; // false by default

마지막으로, 서비스 빌더에 정의 된 포맷터 목록의 순서를 다시 정렬하면 웹 호스트는 목록의 맨 위에 위치하는 포맷터 (예 : 위치 0)로 기본 설정됩니다.

자세한 내용은이 .NET 웹 개발 및 도구 블로그 항목을 참조하십시오.


가장 일반적인 상태 코드에 대해 미리 정의 된 방법이 있습니다.

  • Ok(result)200응답과 함께 반환
  • CreatedAtRoute201+ 새로운 리소스 URL을 반환
  • NotFound 보고 404
  • BadRequest400등을 반환

참조 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).

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

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)
  2. It does content negotiation, i.e. it'll return based on Accept header in request. If Accept: application/xml is sent in request, it'll return as XML. If nothing is sent, JSON is default.

If it needs to send with specific status code, use ObjectResult or StatusCode instead. 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" });

If you specifically want to return as JSON, there are couple of ways

//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.
  2. Both ignores content negotiation.
  3. First method enforces JSON with specific serializer Json(object).
  4. Second method does the same by using Produces() attribute (which is a ResultFilter) with contentType = application/json

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

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

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. Then all my Controller actions use one of the costructors as appropiate. You can take a look at my implementation at: https://github.com/melardev/AspNetCoreApiPaginatedCrud

and

https://github.com/melardev/ApiAspCoreEcommerce

here is how the class looks like(go to my repo for full code):

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.


Awesome answers I found here and I also tried this return statement see StatusCode(whatever code you wish) and it worked!!!

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

Please refer below code, You can manage multiple status code with different type 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.");
    }
}

참고 URL : https://stackoverflow.com/questions/42360139/asp-net-core-return-json-with-status-code

반응형