Retour du code d'état http depuis le contrôleur de L'Api Web
j'essaie de retourner un code d'état 304 non modifié pour une méthode GET dans un contrôleur d'api web.
la seule façon dont j'ai réussi était quelque chose comme ceci:
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;
}
}
le problème ici est que ce n'est pas une exception, c'est juste qu'il n'est pas modifié, donc le cache client est OK. Je veux aussi que le type de retour soit un utilisateur (comme tous les exemples de l'api web avec GET) et non pas le retour du message Httprespeconsemessage ou quelque chose comme ça.
9 réponses
Je ne savais pas la réponse ainsi demandé le ASP.NET équipe ici .
le truc est donc de changer la signature en HttpResponseMessage
et d'utiliser 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);
}
vous pouvez également faire ce qui suit si vous voulez préserver la signature d'action comme utilisateur de retour:
public User GetUser(int userId, DateTime lastModifiedAtClient)
si vous voulez retourner quelque chose d'autre que 200
alors vous lancez un HttpResponseException
dans votre action et passer dans le HttpResponseMessage
que vous voulez envoyer au client.
modifie la méthode de L'API GetXxx pour retourner le message HttpResponseMessage, puis renvoie une version dactylographiée pour la réponse complète et la version non typée pour la réponse 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);
}
}
*les données ClientHasStale sont mon extension pour vérifier les Headers D'ETag et Ifmodified.
le framework MVC doit toujours sérialiser et retourner votre objet.
NOTE
je pense la version générique est en cours de suppression dans une version future de L'API Web.
dans MVC 5, les choses sont devenues plus faciles:
return new StatusCodeResult(HttpStatusCode.NotModified, this);
je déteste cogner de vieux articles, mais c'est le premier résultat pour cela dans la recherche google et j'ai eu un enfer d'un temps avec ce problème (même avec le soutien de vous les gars). Donc, ici, rien ne va...
J'espère que ma solution aidera ceux qui ont aussi été confus.
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;
}
}
}
et puis il suffit d'hériter de la BaseController
[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...
et l'utiliser ensuite
[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;
}
si vous devez retourner un IHttpActionResult et que vous voulez retourner le code d'erreur plus un message, utilisez:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
une autre option:
return new NotModified();
public class NotModified : IHttpActionResult
{
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.NotModified);
return Task.FromResult(response);
}
}
Je n'aime pas avoir à changer ma signature pour utiliser le type HttpCreateResponse, donc j'ai trouvé un peu d'une solution étendue pour cacher cela.
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));
}
}
vous pouvez alors ajouter une méthode à votre ApiController (ou mieux votre contrôleur de base) comme ceci:
protected IHttpActionResult CustomResult(HttpStatusCode code, object data)
{
// Request here is the property on the controller.
return new HttpActionResult(Request, code, data);
}
alors vous pouvez le retourner comme n'importe laquelle des méthodes intégrées:
[HttpPost]
public IHttpActionResult Post(Model model)
{
return model.Id == 1 ?
Ok() :
CustomResult(HttpStatusCode.NotAcceptable, new {
data = model,
error = "The ID needs to be 1."
});
}