Web Api denetleyicisinden http durum kodu döndürme


219

Bir web api denetleyicisinde GET yöntemi için değiştirilmemiş 304 durum kodu döndürmeye çalışıyorum.

Başarabilmemin tek yolu şuydu:

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;
    }
}

Buradaki sorun bunun bir istisna olmaması, sadece istemci önbelleğinin iyi olması için değiştirilmedi. Ayrıca dönüş türü bir kullanıcı (tüm web api örnekleri GET ile gösterildiği gibi) HttpResponseMessage veya bunun gibi bir şey döndürmek istiyorum.


Gece inşa ediyor musunuz betaveya kullanıyor musunuz ?
Aliostad

@Aliostad Beta kullanıyorum
ozba

Peki iade ile ilgili sorun new HttpResponseMessage(HttpStatusCode.NotModified)nedir? Çalışmıyor mu?
Aliostad

@Aliostad Dönüş türü Kullanıcı olduğunda HttpResponseMessage'ı döndüremiyorum, derlemiyor (açıkçası).
ozba

Yanıtlar:


251

Cevabın böylece ASP.NET ekibi istedi bilmiyordum burada .

Yani hile imzayı değiştirmek HttpResponseMessageve kullanmaktır 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);
}

3
CreateResponse parametre olarak yalnızca durum kodunu aldığından ASP.NET MVC 4 beta sürümünde derlenmez. İkincisi, kullanımdan kaldırıldığı için dönüş değeri olarak HttpResponseMessage olmayan bir çözüm istedim: aspnetwebstack.codeplex.com/discussions/350492
ozba

5
Durumda herkes ihtiyacı içinde olacağını kontrol yöntemi değeri elde etmek için GetUser(request, id, lastModified).TryGetContentValue(out user)burada, usera, (örnek durumunda) Usernesne.
Grinn

4
2015'te hala tercih edilen yöntem bu mu? MVC 5?
ezmek

4
Daha modern sürüm IHttpActionResult döndürür - HttpResponseMessage değil (2017)
niico

8
Niico'nun önerisine eklemek için, dönüş türü olduğunda IHttpActionResultve Kullanıcıyı iade etmek istediğinizde, bunu yapabilirsiniz return Ok(user). Başka bir durum kodu döndürmeniz gerekiyorsa (örneğin, yasak) yapabilirsiniz return this.StatusCode(HttpStatusCode.Forbidden).
Drew

68

Eylem imzasını geri dönen Kullanıcı olarak korumak istiyorsanız aşağıdakileri de yapabilirsiniz:

public User GetUser(int userId, DateTime lastModifiedAtClient) 

Başka bir şey döndürmek istiyorsanız , eyleminize 200bir atar ve müşteriye göndermek istediğinizi HttpResponseExceptioniletirsiniz HttpResponseMessage.


9
Bu çok daha şık bir çözüm (albiet eksik cevap). Neden herkes bunu zor yoldan yapmayı tercih ediyor?
nagytech

4
@Geoist stackoverflow.com/questions/1282252/… . İstisna atma maliyeti yüksektir.
tia

10
Evet, yoğun bir API tasarlıyorsanız, en yaygın iletişim durumunu bildirmek için bir istisna kullanmak NotModifiedgerçekten boşa gider. Tüm API'larınız bunu yaptıysa, sunucunuz çoğunlukla wattları istisnalara dönüştürür.
Luke Puplett

2
@nagytech, çünkü bir hata (400 yanıtı gibi) atarsanız özel bir hata mesajı döndüremezsiniz ... Ayrıca kodun yapmasını beklediğiniz bir şey için istisnalar atmak saçmadır. Pahalıdır ve zorunlu olarak olmasını istemediğinizde günlüğe kaydedilir. Gerçekten istisna değiller.
Rocklan

40

MVC 5'te işler kolaylaştı:

return new StatusCodeResult(HttpStatusCode.NotModified, this);

3
Mesaj belirleyemiyor musunuz?
ezmek

1
Bir mesaj kullanmak aslında kabul edilen cevaptır. Bu sadece biraz terser
Jon Bates

39

HttpResponseMessage döndürmek için GetXxx API yöntemini değiştirin ve sonra tam yanıt için yazılan bir sürümü ve NotModified yanıt için türlenmemiş sürümü döndürün.

    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 verileri, ETag ve IfModifiedSince başlıklarını kontrol etmek için kullandığım uzantı.

MVC çerçevesi hala serileştirilmeli ve nesneniz döndürülmelidir.

NOT

Ben genel sürümü Web API gelecekteki bazı sürümünde kaldırılıyor düşünüyorum.


4
Bu aradığım tam cevaptı - Görev <HttpResponseMessage <T>> dönüş türü olsa da. Teşekkürler!
xeb

1
@xeb - evet, bu tamamen söylemeye değer. Async here hakkında daha fazla bilgi asp.net/mvc/tutorials/mvc-4/…
Luke Puplett

14

Eski makaleleri çarpmaktan nefret ediyorum, ancak bu google aramada bunun ilk sonucudur ve bu sorunla ilgili bir zamanım vardı (hatta sizlerin desteği ile). İşte burada hiçbir şey yok ...

Umarım benim çözümüm de karışık olanlara yardımcı olacaktır.

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;
        }
    }
}

ve sonra sadece BaseController'dan miras alın

[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...

ve sonra onu

[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);
}

1
Parlak. Beni bir ton kurtardı.
gls123

10

.net çekirdek 2.2 304 durum kodu döndürüyor. Bu bir ApiController kullanıyor.

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304);
    }

İsteğe bağlı olarak bir nesneyi yanıtla döndürebilirsiniz

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304, YOUROBJECT); 
    }

7

ASP.NET Web Api 2 için, MS'nin bu yayını , yöntemin dönüş türünü değiştirmenizi önerir IHttpActionResult. Daha sonra bir yerleşik dönebilirsiniz IHttpActionResultgibi uygulanması Ok, BadRequestvb ( burada bakınız ) veya kendi uygulamasını dönün.

Kodunuz için şu şekilde yapılabilir:

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

3

Başka seçenek:

return new NotModified();

public class NotModified : IHttpActionResult
{
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotModified);
        return Task.FromResult(response);
    }
}

2
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

Bir IHttpActionResult döndürmeniz gerekiyorsa ve hata kodunu ve bir iletiyi döndürmek istiyorsanız, şunu kullanın:

return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));

2

HttpCreateResponse türünü kullanmak için imzamı değiştirmek zorunda kalmayı sevmiyorum, bu yüzden bunu gizlemek için biraz genişletilmiş bir çözüm buldum.

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

Daha sonra ApiController'ınıza (veya temel kontrol cihazınızı daha iyi) aşağıdaki gibi bir yöntem ekleyebilirsiniz:

protected IHttpActionResult CustomResult(HttpStatusCode code, object data) 
{
    // Request here is the property on the controller.
    return new HttpActionResult(Request, code, data);
}

Ardından, yerleşik yöntemlerden herhangi biri gibi iade edebilirsiniz:

[HttpPost]
public IHttpActionResult Post(Model model)
{
    return model.Id == 1 ?
                Ok() :
                CustomResult(HttpStatusCode.NotAcceptable, new { 
                    data = model, 
                    error = "The ID needs to be 1." 
                });
}

0

@Aliostads için bir güncelleme IHttpActionResult, Web API 2'de sunulan daha fazla moden kullanarak cevap verir .

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

Bunu dene :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.