Returning http status code from Web Api controller

前端 未结 13 1483
一生所求
一生所求 2020-11-27 10:01

I\'m trying to return a status code of 304 not modified for a GET method in a web api controller.

The only way I succeeded was something like this:

p         


        
相关标签:
13条回答
  • 2020-11-27 10:28

    For ASP.NET Web Api 2, this post from MS suggests to change the method's return type to IHttpActionResult. You can then return a built in IHttpActionResult implementation like Ok, BadRequest, etc (see here) or return your own implementation.

    For your code, it could be done like:

    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);
    }
    
    0 讨论(0)
  • 2020-11-27 10:29

    I did not know the answer so asked the ASP.NET team here.

    So the trick is to change the signature to HttpResponseMessage and use 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);
    }
    
    0 讨论(0)
  • 2020-11-27 10:33

    I hate bumping old articles but this is the first result for this in google search and I had a heck of a time with this problem (even with the support of you guys). So here goes nothing...

    Hopefully my solution will help those that also was confused.

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

    and then just inherit from the BaseController

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

    and then using it

    [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);
    }
    
    0 讨论(0)
  • 2020-11-27 10:34

    I don't like having to change my signature to use the HttpCreateResponse type, so I came up with a little bit of an extended solution to hide that.

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

    You can then add a method to your ApiController (or better your base controller) like this:

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

    Then you can return it just like any of the built in methods:

    [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 讨论(0)
  • 2020-11-27 10:36

    Change the GetXxx API method to return HttpResponseMessage and then return a typed version for the full response and the untyped version for the NotModified response.

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

    *The ClientHasStale data is my extension for checking ETag and IfModifiedSince headers.

    The MVC framework should still serialize and return your object.

    NOTE

    I think the generic version is being removed in some future version of the Web API.

    0 讨论(0)
  • 2020-11-27 10:36

    Try this :

    return new ContentResult() { 
        StatusCode = 404, 
        Content = "Not found" 
    };
    
    0 讨论(0)
提交回复
热议问题