상태 코드가 포함 된 ASP.NET Core 반환 JSON


153

.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을 반환하기를 원합니다.


1
"ok"메소드는 200을 상태 코드로 리턴합니다. 사전 정의 된 방법은 모든 일반적인 경우를 다룹니다. 201을 반환하려면 (+ 새로운 리소스 위치의 헤더) CreatedAtRoute방법 등 을 사용 합니다.
Tseng

답변:


191

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()메서드 의 복제본입니다 . 그러나 템플릿이 포함 된 사전 배송 된 서비스를 사용하는 대신 각각의 모든 서비스를 정의하여 각 "기본"서비스를 자체적으로 구현하고 있습니다. 코드 블록에 리포지토리 링크를 추가했거나 GitHub 리포지토리에서 확인할 수 있습니다 AddMvc() ..

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


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

특정 형식 강제 적용 가능한 특정 작업에 대한 응답 형식을 제한하려면 [제작] 필터를 적용 할 수 있습니다. [Produces] 필터는 특정 작업 (또는 컨트롤러)에 대한 응답 형식을 지정합니다. 대부분의 필터와 마찬가지로이 값은 작업, 컨트롤러 또는 전역 범위에서 적용 할 수 있습니다.

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

[Produces]필터는 내의 모든 작업을 강제로 AuthorsController다른 포매터가 응용 프로그램에 대한 구성 및 클라이언트가 제공 한 경우에도, JSON 형식의 응답을 반환하는 Accept다른 가능한 형식을 요청 헤더를.

방법 2 선호하는 방법은 WebAPI가 요청 된 형식으로 모든 요청에 ​​응답하는 것입니다. 그러나,이 요구 된 형식을 허용하지 않는 경우에, 다음 가을 - 다시 초기 상태로 (즉. JSON)

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

options.RespectBrowserAcceptHeader = true; // false by default

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

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


고맙습니다 정말이 넣어 노력하십시오. 당신의 대답은 저를 구현하는 데 영감을 IActionResultreturn Ok(new {response = "123"});건배!
Rossco

1
@Rossco 문제 없습니다. 코드의 나머지 부분이 프로젝트 개발에 도움이되기를 바랍니다.
Svek February

1
이 주제를 확장하기 위해 WebAPI 구현에 대한 추가적이고 완전한 가이드를 작성했습니다. stackoverflow.com/q/42365275/3645638
Svek

설정시 : RespectBrowserAcceptHeader = true; 왜 그런 일을하는지 설명하고 있지 않으며 일반적으로 불필요하고 잘못하는 것입니다. 브라우저는 html을 요청하므로 어쨌든 포맷터 선택에 영향을 미치지 않아야합니다 (크롬은 XML을 요구하여 불행히도 수행합니다). 요컨대 그것은 내가 지키지 않을 것이고, 당신이 지정하는 대체는 이미 기본 행동입니다.
Yishai Galatzer

@YishaiGalatzer 내 답변의 해당 부분의 주요 주제는 클라이언트와 API 논리 사이에 기본 미들웨어를 부담 해제하는 방법을 강조하는 것이 었습니다. 내 생각에, RespectBrowserAcceptHeader대체 직렬 변환기 사용을 구현할 때 또는 더 일반적으로 클라이언트가 잘못된 요청을 보내지 않도록하려는 경우 중요합니다. 따라서 "프로젝트를 완전히 제어 해야하고 서비스를 엄격하게 정의하려는 경우"를 강조 하고 해당 문구 위에 강조 표시된 블록 인용문도 적어 둡니다.
Svek

57

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

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

참조 BaseController.csController.cs모든 메소드의 목록.

그러나 정말로 주장한다면 StatusCode사용자 정의 코드를 설정하는 데 사용할 수는 있지만 코드를 읽을 수 없게 만들고 헤더를 설정하기 위해 코드를 반복해야합니다 (와 같은 CreatedAtRoute).

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

1
이것은 아래의 응답에 대한 통찰력을주었습니다. 감사합니다
Oge Nwike

이 코드는 ASP.NET Core 2.2에 적합하지 않습니다. 난 그냥 그것을 시도하고 그것으로 직렬화 에 의해 생성 방법. "123"문자열은 직접 포함하지 않습니다. JSONActionResultJson()
amedina

1
@amedina : 내 나쁜 단지를 제거 Json(...)하고에 문자열을 전달 StatusCode

"확인 (결과)"이라고 말하면 결과는 무엇입니까? JSON 형식 문자열입니까, 아니면 자동으로 JSON 문자열로 변환되는 C # 개체입니까?
변수

@variable : 항상 POCO / class / object입니다. 당신이 문자열을 반환하려는 경우, 당신은 대신 "콘텐츠"를 사용합니다

43

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기본값입니다.

특정 상태 코드와 함께 보내야 하는 ObjectResult경우 StatusCode또는를 사용하십시오. 둘 다 동일한 작업을 수행하고 컨텐츠 협상을 지원합니다.

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. 첫 번째 방법은 특정 serializer로 JSON을 적용 Json(object)합니다.
  4. 두 번째 방법은 Produces()속성 ( ResultFilter)을contentType = application/json

공식 문서 에서 자세한 내용을 읽어보십시오 . 여기에서 필터에 대해 알아보십시오 .

샘플에 사용되는 간단한 모델 클래스

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

10
이것은 질문에 초점을 맞추고 일부 실용성을 간략하게 설명하기 때문에 좋은 대답입니다.
netfed

33

내가 생각해 낸 가장 쉬운 방법은 다음과 같습니다.

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

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

2
그의 솔루션에는 상태 코드 등을위한 중복 필드가 포함되어 있기 때문에 이것이 @tseng의 답변보다 낫다고 생각합니다.
Christian Sauer

2
다음과 같이 Microsoft.AspNetCore.Http에 정의 된 StatusCode를 사용하는 것이 개선 될 수 있습니다. return new JsonResult (new {}) {StatusCode = StatusCodes.Status404NotFound};
Bryan Bedard

2
이것이 정답입니다. json을 보편적으로 설정하는 방법이 있지만 때로는 레거시 엔드 포인트와 작업해야하기 때문에 설정이 다를 수 있습니다. 레거시 엔드 포인트 지원을 중단 할 수있을 때까지는 이것이 완전히 제어 할 수있는 궁극적 인 방법입니다.
pqsk

Microsoft.AspNetCore.Mvc.JsonResult는 내가 생각하는 정규화 된 이름입니다. FQN 또는 "사용 중"이 없으면 답이 없습니다. :) 어셈블리 Microsoft.AspNetCore.Mvc.Core, 버전 = 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
granadaCoder

1
이것은 강력한 유형 (이 예제에서 "ITem result = new Item"... 항목은 런타임에 알려진 유형입니다)이있을 때 나를 위해 일했습니다. 유형을 알 수없는시기에 대한 내 대답 (이 질문에 대한)을 참조하십시오. (DB에 json이 있었고 런타임에 json 유형을 알 수 없었습니다). 고마워 제럴드
granadaCoder

15

이것이 가장 쉬운 해결책입니다.

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

또는

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

4

enum을 사용하여 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 !!!"));
    }

열거 형은 좋은 생각입니다!.
Bhimbim

2

내가 여기서 찾은 멋진 대답과 나는이 반환 진술을 보았고 StatusCode(whatever code you wish)효과가있었습니다 !!!

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

1
이 같은! 좋은 제안!
ticky

0

아래 코드를 참조하십시오. 다른 유형의 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.");
    }
}

9
아뇨.
Phillip Copley

0

내 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)를 주목하십시오.


0

나는 이것을 작동시켰다. 내 큰 문제는 내 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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.