type net ihttpactionresult httpresponsemessage handling exceptions error catch asp all c# asp.net-web-api httpresponse http-status-codes

c# - net - web api error handling



Devolver código de estado http desde el controlador Web Api (9)

Cambie el método GetXxx API para devolver HttpResponseMessage y luego devuelva una versión mecanografiada para la respuesta completa y la versión sin tipo para la respuesta 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); } }

* Los datos de ClientHasStale son mi extensión para verificar los encabezados de ETag y IfModifiedSince.

El framework MVC aún debe serializar y devolver su objeto.

NOTA

Creo que la versión genérica se eliminará en alguna versión futura de la API web.

Estoy tratando de devolver un código de estado de 304 no modificado para un método GET en un controlador de API web.

La única forma en que tuve éxito fue algo como esto:

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

El problema aquí es que no es una excepción, simplemente no se modifica, por lo que la memoria caché del cliente está bien. También quiero que el tipo de devolución sea un Usuario (como todos los ejemplos de la API web muestran con GET) no devuelve HttpResponseMessage o algo así.


En MVC 5, las cosas se pusieron más fáciles:

return new StatusCodeResult(HttpStatusCode.NotModified, this);


No me gusta tener que cambiar mi firma para usar el tipo HttpCreateResponse, así que se me ocurrió una pequeña solución extendida para ocultar eso.

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

A continuación, puede agregar un método a su ApiController (o mejor su controlador base) de esta manera:

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

Luego puede devolverlo como cualquiera de los métodos integrados:

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


No sabía la respuesta, así que le pregunté al equipo de ASP.NET here .

Entonces, el truco es cambiar la firma a HttpResponseMessage y usar 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); }


Odio chocar con los artículos antiguos, pero este es el primer resultado para esto en la búsqueda de Google y tuve un gran problema con este problema (incluso con el apoyo de ustedes). Entonces aquí no pasa nada ...

Espero que mi solución ayude a aquellos que también estaban confundidos.

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

y luego simplemente heredar de BaseController

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

y luego usarlo

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


Otra opción:

return new NotModified();

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


Si necesita devolver un IHttpActionResult y desea devolver el código de error más un mensaje, use:

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


También puede hacer lo siguiente si desea conservar la firma de acción como usuario que regresa:

public User GetUser(int userId, DateTime lastModifiedAtClient)

Si desea devolver algo que no sea 200 , HttpResponseException una HttpResponseException en su acción y pasa el HttpResponseMessage que desea enviar al cliente.


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