Возврат кода статуса http из Web Api controller
Я пытаюсь вернуть код состояния 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 или что-то вроде этого.
Ответы
Ответ 1
Я не знал ответа, поэтому попросил команду 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);
}
Ответ 2
Вы также можете сделать следующее, если хотите сохранить сигнатуру действия в качестве возвращаемого Пользователь:
public User GetUser(int userId, DateTime lastModifiedAtClient)
Если вы хотите вернуть что-то отличное от 200
, то вы выбросите HttpResponseException
в свое действие и передаете в HttpResponseMessage
, который хотите отправить клиенту.
Ответ 3
Измените метод API GetXxx, чтобы вернуть 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 должна по-прежнему сериализовать и возвращать ваш объект.
Примечание
Я думаю, что общая версия удаляется в некоторой будущей версии веб-API.
Ответ 4
В MVC 5 все стало проще:
return new StatusCodeResult(HttpStatusCode.NotModified, this);
Ответ 5
Мне не нравится набрасывать старые статьи, но это первый результат для поиска в 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);
}
Ответ 6
.net core 2.2 возвращает код состояния 304. Это использует ApiController.
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304);
}
При желании вы можете вернуть объект с ответом
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304, YOUROBJECT);
}
Ответ 7
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;
}
Ответ 8
Если вам нужно вернуть IHttpActionResult и хотите вернуть код ошибки плюс сообщение, используйте:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
Ответ 9
Другая опция:
return new NotModified();
public class NotModified : IHttpActionResult
{
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.NotModified);
return Task.FromResult(response);
}
}
Ответ 10
В 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);
}
Ответ 11
Мне не нравится менять свою подпись, чтобы использовать тип 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."
});
}
Ответ 12
Обновление @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);
}
}
Ответ 13
Попробуйте это:
return new ContentResult() {
StatusCode = 404,
Content = "Not found"
};