Web Api 컨트롤러에서 http 상태 코드 반환


219

웹 API 컨트롤러에서 GET 메소드에 대해 수정되지 않은 상태 코드 304를 반환하려고합니다.

내가 성공한 유일한 방법은 다음과 같습니다.

public class TryController : ApiController
{
    public User GetUser(int userId, DateTime lastModifiedAtClient)
    {
        var user = new DataEntities().Users.First(p => p.Id == userId);
        if (user.LastModified <= lastModifiedAtClient)
        {
             throw new HttpResponseException(HttpStatusCode.NotModified);
        }
        return user;
    }
}

여기서 문제는 예외가 아니라 클라이언트 캐시가 정상이므로 수정되지 않았기 때문입니다. 또한 반환 유형이 사용자가되기를 원합니다 (모든 웹 API 예제는 GET으로 표시됨) HttpResponseMessage 또는 이와 유사한 것을 반환하지 않습니다.


당신이 사용하고 beta또는 야간 빌드 ?
Aliostad

@Aliostad 저는 베타
ozba를

그래서 반환에 어떤 문제가 new HttpResponseMessage(HttpStatusCode.NotModified)있습니까? 작동하지 않습니까?
Aliostad

@ Aliostad 반환 유형이 User 인 경우 HttpResponseMessage를 반환 할 수 없으며 컴파일되지 않습니다 (분명히).
ozba

답변:


251

나는 대답을 몰랐기 때문에 여기 에서 ASP.NET 팀에 물었다 .

따라서 트릭은로 서명을 변경 HttpResponseMessage하고 사용하는 것 Request.CreateResponse입니다.

[ResponseType(typeof(User))]
public HttpResponseMessage GetUser(HttpRequestMessage request, int userId, DateTime lastModifiedAtClient)
{
    var user = new DataEntities().Users.First(p => p.Id == userId);
    if (user.LastModified <= lastModifiedAtClient)
    {
         return new HttpResponseMessage(HttpStatusCode.NotModified);
    }
    return request.CreateResponse(HttpStatusCode.OK, user);
}

3
CreateResponse는 상태 코드 만 매개 변수로 사용하므로 ASP.NET MVC 4 베타 릴리스에서는 컴파일되지 않습니다. 두 번째로, HttpResponseMessage가없는 솔루션은 더 이상 사용되지 않으므로 반환 값으로 사용하고 싶었습니다. aspnetwebstack.codeplex.com/discussions/350492
ozba

5
경우 모든 사용자가 필요에 것 제어기 방법의 값을 얻는 GetUser(request, id, lastModified).TryGetContentValue(out user)경우, userA는 (예를 경우) User개체.
Grinn

4
이것이 2015 년에도 여전히 선호되는 방법입니까? MVC 5?
호감

4
더 현대적인 버전은 IHttpActionResult을 반환 -하지 HttpResponseMessage (2017)
niico

8
niico의 제안에 추가하려면 반환 유형이 IHttpActionResult사용자이고 사용자를 반환하려는 경우을 수행하면 return Ok(user)됩니다. 다른 상태 코드 (예 : 금지)를 반환해야하는 경우을 수행하면 return this.StatusCode(HttpStatusCode.Forbidden)됩니다.
Drew

68

조치 서명을 리턴 사용자로 유지하려는 경우 다음을 수행 할 수도 있습니다.

public User GetUser(int userId, DateTime lastModifiedAtClient) 

당신이 다른 것을 반환하고 싶다면 , 당신은 당신의 행동 200을 던져서 당신이 클라이언트에게 보내고 싶은 것을 전달합니다.HttpResponseExceptionHttpResponseMessage


9
이것은 더 우아한 해결책입니다 (불완전한 답변이 있습니다). 왜 모두가 어려운 일을 선호합니까?
nagytech

4
@Geoist stackoverflow.com/questions/1282252/… . 예외를 던지면 비용이 많이 듭니다.
tia

10
예, 바쁜 API를 디자인하는 경우 예외를 사용하여 가장 일반적인 경우를 전달하는 NotModified것은 실제로 낭비입니다. 모든 API가이 작업을 수행 한 경우 서버는 대부분 와트를 예외로 변환합니다.
Luke Puplett

2
@nagytech (400 응답과 같은) 오류를 던지면 사용자 정의 오류 메시지를 반환 할 수 없기 때문에 예외를 던지는 것은 코드가 기대하는 것에 어리 석습니다. 비용이 많이 들며 필요하지 않을 때 기록됩니다. 그들은 실제로 예외는 아닙니다.
Rocklan

40

MVC 5에서는 일이 쉬워졌습니다.

return new StatusCodeResult(HttpStatusCode.NotModified, this);

3
메시지를 지정할 수 없습니까?
호감

1
메시지를 사용하는 것이 실제로 허용되는 답변입니다. 이것은 조금 terser입니다
존 베이츠

39

GetXxx API 메소드를 변경하여 HttpResponseMessage를 리턴 한 후 전체 응답에 대한 유형화 된 버전과 NotModified 응답에 대한 유형화되지 않은 버전을 리턴하십시오.

    public HttpResponseMessage GetComputingDevice(string id)
    {
        ComputingDevice computingDevice =
            _db.Devices.OfType<ComputingDevice>()
                .SingleOrDefault(c => c.AssetId == id);

        if (computingDevice == null)
        {
            return this.Request.CreateResponse(HttpStatusCode.NotFound);
        }

        if (this.Request.ClientHasStaleData(computingDevice.ModifiedDate))
        {
            return this.Request.CreateResponse<ComputingDevice>(
                HttpStatusCode.OK, computingDevice);
        }
        else
        {
            return this.Request.CreateResponse(HttpStatusCode.NotModified);
        }
    }

* ClientHasStale 데이터는 ETag 및 IfModifiedSince 헤더를 확인하기위한 확장입니다.

MVC 프레임 워크는 여전히 개체를 직렬화하고 반환해야합니다.

노트

향후 버전의 Web API에서 일반 버전이 제거되고 있다고 생각합니다.


4
Task <HttpResponseMessage <T >> 반환 유형이지만 이것이 내가 찾던 정확한 답변이었습니다. 감사!
xeb

1
@xeb-그렇습니다. 완전히 불러 낼 가치가 있습니다. 여기에 비동기에 대한 자세한 정보를 원하시면 asp.net/mvc/tutorials/mvc-4/...
누가 복음 Puplett

14

나는 오래된 기사를 부딪 치는 것을 싫어하지만 이것은 Google 검색 에서이 첫 번째 결과 이며이 문제에 대해 많은 시간을 보냈습니다 (여러분의 지원조차도). 그래서 여기에 아무 것도 가지 않습니다 ...

내 솔루션이 혼란스러워하는 사람들을 도울 수 있기를 바랍니다.

namespace MyApplication.WebAPI.Controllers
{
    public class BaseController : ApiController
    {
        public T SendResponse<T>(T response, HttpStatusCode statusCode = HttpStatusCode.OK)
        {
            if (statusCode != HttpStatusCode.OK)
            {
                // leave it up to microsoft to make this way more complicated than it needs to be
                // seriously i used to be able to just set the status and leave it at that but nooo... now 
                // i need to throw an exception 
                var badResponse =
                    new HttpResponseMessage(statusCode)
                    {
                        Content =  new StringContent(JsonConvert.SerializeObject(response), Encoding.UTF8, "application/json")
                    };

                throw new HttpResponseException(badResponse);
            }
            return response;
        }
    }
}

그런 다음 BaseController에서 상속받습니다.

[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...

그런 다음 사용

[HttpGet]
[Route("device/search/{property}/{value}")]
public SearchForDeviceResponse SearchForDevice(string property, string value)
{
    //todo: limit search property here?
    var response = new SearchForDeviceResponse();

    var results = _deviceManagementBusiness.SearchForDevices(property, value);

    response.Success = true;
    response.Data = results;

    var statusCode = results == null || !results.Any() ? HttpStatusCode.NoContent : HttpStatusCode.OK;

    return SendResponse(response, statusCode);
}

1
훌륭한. 많은 시간을 절약했습니다.
gls123

10

.net core 2.2에서 304 상태 코드를 반환합니다. 이것은 ApiController를 사용하고 있습니다.

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304);
    }

선택적으로 응답과 함께 객체를 반환 할 수 있습니다

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304, YOUROBJECT); 
    }

7

ASP.NET Web Api 2의 경우 MS 의이 게시물 은 메소드의 반환 유형을로 변경하도록 제안합니다 IHttpActionResult. 그런 다음에 내장을 반환 할 수 있습니다 IHttpActionResult와 같은 구현 Ok, BadRequest등 ( 여기 참조 ) 또는 자신의 구현을 반환합니다.

코드의 경우 다음과 같이 수행 할 수 있습니다.

public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
    var user = new DataEntities().Users.First(p => p.Id == userId);
    if (user.LastModified <= lastModifiedAtClient)
    {
        return StatusCode(HttpStatusCode.NotModified);
    }
    return Ok(user);
}

3

다른 옵션 :

return new NotModified();

public class NotModified : IHttpActionResult
{
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotModified);
        return Task.FromResult(response);
    }
}

2
public HttpResponseMessage Post(Article article)
{
    HttpResponseMessage response = Request.CreateResponse<Article>(HttpStatusCode.Created, article);

    string uriToTheCreatedItem = Url.Route(null, new { id = article.Id });
    response.Headers.Location = new Uri(Request.RequestUri, uriToTheCreatedItem);

    return response;
}

2

IHttpActionResult를 리턴해야하고 오류 코드와 메시지를 리턴하려면 다음을 사용하십시오.

return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));

2

HttpCreateResponse 유형을 사용하기 위해 서명을 변경하는 것을 좋아하지 않으므로이를 숨기기 위해 약간의 확장 솔루션을 생각해 냈습니다.

public class HttpActionResult : IHttpActionResult
{
    public HttpActionResult(HttpRequestMessage request) : this(request, HttpStatusCode.OK)
    {
    }

    public HttpActionResult(HttpRequestMessage request, HttpStatusCode code) : this(request, code, null)
    {
    }

    public HttpActionResult(HttpRequestMessage request, HttpStatusCode code, object result)
    {
        Request = request;
        Code = code;
        Result = result;
    }

    public HttpRequestMessage Request { get; }
    public HttpStatusCode Code { get; }
    public object Result { get; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Request.CreateResponse(Code, Result));
    }
}

그런 다음 다음과 같이 ApiController (또는 더 나은 기본 컨트롤러)에 메소드를 추가 할 수 있습니다.

protected IHttpActionResult CustomResult(HttpStatusCode code, object data) 
{
    // Request here is the property on the controller.
    return new HttpActionResult(Request, code, data);
}

그런 다음 내장 메소드와 마찬가지로 반환 할 수 있습니다.

[HttpPost]
public IHttpActionResult Post(Model model)
{
    return model.Id == 1 ?
                Ok() :
                CustomResult(HttpStatusCode.NotAcceptable, new { 
                    data = model, 
                    error = "The ID needs to be 1." 
                });
}

0

@Aliostads에 대한 업데이트 IHttpActionResult는 Web API 2에 도입 된 더 많은 모뎀을 사용하여 답변합니다 .

https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/action-results#ihttpactionresult

public class TryController : ApiController
{
    public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
    {
        var user = new DataEntities().Users.First(p => p.Id == userId);
        if (user.LastModified <= lastModifiedAtClient)
        {
            return StatusCode(HttpStatusCode.NotModified);
            // If you would like to return a Http Status code with any object instead:
            // return Content(HttpStatusCode.InternalServerError, "My Message");
        }
        return Ok(user);
    }
}

0

이 시도 :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.