webentwicklung-frage-antwort-db.com.de

Rückgabe des http-Statuscodes vom Web Api-Controller

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.

207
ozba

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

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.

39
Luke Puplett

In MVC 5 wurde es einfacher:

return new StatusCodeResult(HttpStatusCode.NotModified, this);
37
Jon Bates

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);
}
12
Kenneth Garza

.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); 
    }
5
Ives.me
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;
}
2
Jo Smo

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

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"));
2
Chris Halcrow

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);
    }
}
2
Bora Aydın

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." 
                });
}
1
krillgar

Ein Update für @Aliostads wird mit dem in Web API 2 eingeführten moden IHttpActionResult beantwortet.

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