Возврат кода состояния http из контроллера Web Api
Я пытаюсь вернуть код состояния 304 не изменен для метода GET в контроллере web api.
единственный способ, которым я преуспел было что-то вроде этого:
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 или что-то в этом роде.
9 ответов:
Я не знал ответа, поэтому спросил 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); }
вы также можете сделать следующее, Если вы хотите сохранить подпись действия как возврат пользователя:
public User GetUser(int userId, DateTime lastModifiedAtClient)
если вы хотите вернуть что-то другое, чем
200
тогда вы бросаетеHttpResponseException
в вашем действии и пройти вHttpResponseMessage
вы хотите отправить клиенту.
измените метод GETXXX API для возврата HttpResponseMessage, а затем верните типизированную версию для полного ответа и нетипизированную версию для немодифицированного ответа.
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 все равно должна сериализовать и вернуть ваш объект.
Примечание
Я думаю, что общая версия удаляется в какой-то будущей версии из веб-API.
Я ненавижу натыкаться на старые статьи, но это первый результат для этого в поиске 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); }
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; }
Если вам нужно вернуть IHttpActionResult и хотите вернуть код ошибки плюс сообщение, используйте:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
другой вариант:
return new NotModified();
public class NotModified : IHttpActionResult { public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { var response = new HttpResponseMessage(HttpStatusCode.NotModified); return Task.FromResult(response); } }
мне не нравится менять свою подпись, чтобы использовать тип 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." }); }