Ich versuche, einen Statuscode 304 zurückzugeben, der für eine GET-Methode in einem Web-API-Controller nicht geändert wurde.
Der einzige Weg, auf dem ich Erfolg hatte, war folgender:
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;
}
}
Das Problem hierbei ist, dass dies keine Ausnahme ist. Es wurde nur nicht geändert, sodass der Client-Cache in Ordnung ist. Ich möchte auch, dass der Rückgabetyp ein Benutzer ist (wie alle Web-API-Beispiele mit GET zeigen) und nicht HttpResponseMessage oder so etwas zurückgibt.
Ich wusste die Antwort nicht, also fragte das ASP.NET-Team hier .
Der Trick besteht also darin, die Signatur in HttpResponseMessage
zu ändern und Request.CreateResponse
Zu verwenden.
[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);
}
Sie können auch Folgendes tun, wenn Sie die Aktionssignatur als wiederkehrenden Benutzer beibehalten möchten:
public User GetUser(int userId, DateTime lastModifiedAtClient)
Wenn Sie etwas anderes als 200
Zurückgeben möchten, werfen Sie ein HttpResponseException
in Ihre Aktion und übergeben das HttpResponseMessage
, das Sie an den Client senden möchten.
Ändern Sie die GetXxx-API-Methode, um HttpResponseMessage zurückzugeben, und geben Sie dann eine typisierte Version für die vollständige Antwort und die untypisierte Version für die NotModified-Antwort zurück.
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);
}
}
* Die ClientHasStale-Daten sind meine Erweiterung zum Überprüfen von ETag- und IfModifiedSince-Headern.
Das MVC-Framework sollte Ihr Objekt weiterhin serialisieren und zurückgeben.
NOTE
Ich denke, dass die generische Version in einigen zukünftigen Versionen der Web-API entfernt wird.
In MVC 5 wurde es einfacher:
return new StatusCodeResult(HttpStatusCode.NotModified, this);
Ich hasse es, alte Artikel anzustoßen, aber dies ist das erste Ergebnis in der Google-Suche, und ich hatte eine verdammte Zeit mit diesem Problem (auch mit der Unterstützung von euch). Also hier geht nichts ...
Hoffentlich hilft meine Lösung denen, die auch verwirrt waren.
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;
}
}
}
und dann einfach vom BaseController erben
[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...
und dann benutzen
[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);
}
.net Core 2.2, das den 304-Statuscode zurückgibt. Dies verwendet einen ApiController.
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304);
}
Optional können Sie ein Objekt mit der Antwort zurückgeben
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304, YOUROBJECT);
}
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;
}
Für ASP.NET Web Api 2 empfiehlt dieser Beitrag von MS , den Rückgabetyp der Methode in IHttpActionResult
zu ändern. Sie können dann eine eingebaute IHttpActionResult
-Implementierung wie Ok
, BadRequest
usw. ( siehe hier ) oder Ihre eigene Implementierung zurückgeben.
Für Ihren Code könnte dies folgendermaßen aussehen:
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);
}
Wenn Sie ein IHttpActionResult zurückgeben müssen und den Fehlercode sowie eine Nachricht zurückgeben möchten, verwenden Sie:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
Andere Option:
return new NotModified();
public class NotModified : IHttpActionResult
{
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.NotModified);
return Task.FromResult(response);
}
}
Ich mag es nicht, meine Signatur ändern zu müssen, um den HttpCreateResponse-Typ zu verwenden, deshalb habe ich mir eine etwas erweiterte Lösung ausgedacht, um das zu verbergen.
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));
}
}
Anschließend können Sie Ihrem ApiController (oder besser Ihrem Basiscontroller) eine Methode wie folgt hinzufügen:
protected IHttpActionResult CustomResult(HttpStatusCode code, object data)
{
// Request here is the property on the controller.
return new HttpActionResult(Request, code, data);
}
Dann können Sie es wie jede der eingebauten Methoden zurückgeben:
[HttpPost]
public IHttpActionResult Post(Model model)
{
return model.Id == 1 ?
Ok() :
CustomResult(HttpStatusCode.NotAcceptable, new {
data = model,
error = "The ID needs to be 1."
});
}
Ein Update für @Aliostads wird mit dem in Web API 2 eingeführten moden IHttpActionResult
beantwortet.
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);
}
}