Trả lại mã trạng thái http từ bộ điều khiển Api Web


219

Tôi đang cố gắng trả lại mã trạng thái 304 không được sửa đổi cho phương thức GET trong bộ điều khiển api web.

Cách duy nhất tôi thành công là một cái gì đó như thế này:

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

Vấn đề ở đây là nó không phải là ngoại lệ, chỉ là nó không được sửa đổi nên bộ đệm của máy khách vẫn ổn. Tôi cũng muốn loại trả về là Người dùng (vì tất cả các ví dụ api trên web hiển thị với GET) không trả về HttpResponseMessage hoặc đại loại như thế này.


Bạn đang sử dụng betahoặc xây dựng hàng đêm ?
Aliostad

@Aliostad Tôi đang sử dụng bản beta
ozba

Vì vậy, có gì sai khi trở về new HttpResponseMessage(HttpStatusCode.NotModified)? Nó không hoạt động?
Aliostad

@Aliostad Tôi không thể trả về HttpResponseMessage khi loại trả về là Người dùng, nó không được biên dịch (rõ ràng).
ozba

Câu trả lời:


251

Tôi không biết câu trả lời nên đã hỏi nhóm ASP.NET ở đây .

Vì vậy, mẹo là thay đổi chữ ký HttpResponseMessagevà sử dụng 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
Nó không biên dịch trong bản phát hành ASP.NET MVC 4 beta, vì CreatResponse chỉ lấy mã trạng thái làm tham số. thứ hai, tôi muốn một giải pháp không có httpResponseMessage làm giá trị trả về vì nó không được dùng nữa: aspnetwebstack.codeplex.com/discussions/350492
ozba

5
Trong trường hợp bất cứ ai cần nó, để có được giá trị từ phương thức điều khiển sẽ là GetUser(request, id, lastModified).TryGetContentValue(out user), trong đó user(trong trường hợp ví dụ) là một Userđối tượng.
Grinn

4
Đây có phải vẫn là phương pháp được ưa thích trong năm 2015? MVC 5?
nghiền nát

4
Phiên bản hiện đại hơn trả về IHttpActionResult - không phải là httpResponseMessage (2017)
niico

8
Để thêm vào đề xuất của niico, khi loại trả về là IHttpActionResultvà bạn muốn trả về Người dùng, bạn chỉ cần làm return Ok(user). Nếu bạn cần trả về một mã trạng thái khác (giả sử, bị cấm), bạn có thể làm điều đó return this.StatusCode(HttpStatusCode.Forbidden).
Drew

68

Bạn cũng có thể thực hiện các thao tác sau nếu muốn giữ chữ ký hành động khi trả về Người dùng:

public User GetUser(int userId, DateTime lastModifiedAtClient) 

Nếu bạn muốn trả lại một cái gì đó khác hơn 200thì bạn ném một HttpResponseExceptionhành động của bạn và chuyển vào thứ HttpResponseMessagebạn muốn gửi cho khách hàng.


9
Đây là một cách giải pháp thanh lịch hơn (câu trả lời không đầy đủ của albiet). Tại sao mọi người thích làm điều đó một cách khó khăn?
nagytech

4
@Geoist stackoverflow.com/questions/1282252/ . Ném ngoại lệ là tốn kém.
tia

10
Vâng, nếu bạn đang thiết kế một API bận rộn, sử dụng một ngoại lệ để truyền đạt trường hợp phổ biến nhất NotModifiedlà thực sự lãng phí. Nếu tất cả các API của bạn đã làm điều này, thì máy chủ của bạn sẽ chủ yếu chuyển đổi watts thành ngoại lệ.
Luke Puplett

2
@nagytech bởi vì bạn không thể trả về thông báo lỗi tùy chỉnh nếu bạn ném lỗi (như phản hồi 400) ... việc ném ngoại lệ là ngớ ngẩn đối với một cái gì đó mà bạn mong đợi mã sẽ làm. Đắt tiền và sẽ được ghi lại khi bạn không nhất thiết muốn chúng. Họ không thực sự ngoại lệ.
Rocklan

40

Trong MVC 5, mọi thứ trở nên dễ dàng hơn:

return new StatusCodeResult(HttpStatusCode.NotModified, this);

3
Không thể chỉ định một tin nhắn?
nghiền nát

1
Sử dụng một tin nhắn thực sự là câu trả lời được chấp nhận. Đây chỉ là một chút khó khăn
Jon Bates

39

Thay đổi phương thức API GetXxx để trả về HttpResponseMessage và sau đó trả về một phiên bản đã nhập cho phản hồi đầy đủ và phiên bản chưa được đánh dấu cho phản hồi 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);
        }
    }

* Dữ liệu ClientHasStale là tiện ích mở rộng của tôi để kiểm tra các tiêu đề ETag và IfModifiedSince.

Khung MVC vẫn nên tuần tự hóa và trả về đối tượng của bạn.

GHI CHÚ

Tôi nghĩ rằng phiên bản chung đang bị xóa trong một số phiên bản tương lai của API Web.


4
Đây là câu trả lời chính xác mà tôi đang tìm kiếm - mặc dù là loại trả về <httpResponseMessage <T >>. Cảm ơn!
xeb

1
@xeb - vâng, điều đó hoàn toàn đáng để gọi ra. Thông tin thêm về async đây asp.net/mvc/tutorials/mvc-4/...
Luke Puplett

14

Tôi ghét phải trả lại các bài viết cũ nhưng đây là kết quả đầu tiên cho việc tìm kiếm trên google và tôi đã có một khoảng thời gian với vấn đề này (ngay cả với sự hỗ trợ của các bạn). Vì vậy, ở đây không có gì ...

Hy vọng giải pháp của tôi sẽ giúp những người cũng bị nhầm lẫn.

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

và sau đó chỉ kế thừa từ BaseContoder

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

và sau đó sử dụng nó

[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
Xuất sắc. Tiết kiệm cho tôi một tấn thời gian.
gls123

10

.net core 2.2 trả về mã trạng thái 304. Đây là sử dụng một ApiControll.

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

Tùy chọn bạn có thể trả về một đối tượng với phản hồi

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

7

Đối với ASP.NET Web Api 2, bài đăng này từ MS đề nghị thay đổi kiểu trả về của phương thức thành IHttpActionResult. Sau đó bạn có thể trở lại một xây dựng trong IHttpActionResultthực hiện như Ok, BadRequest, vv ( xem tại đây ) hoặc trả lại thực hiện của riêng bạn.

Đối với mã của bạn, nó có thể được thực hiện như sau:

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

Một lựa chọn khác:

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

Nếu bạn cần trả về IHttpActionResult và muốn trả về mã lỗi cộng với một thông báo, hãy sử dụng:

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

2

Tôi không muốn phải thay đổi chữ ký của mình để sử dụng loại HttpCreateResponse, vì vậy tôi đã đưa ra một chút giải pháp mở rộng để che giấu điều đó.

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

Sau đó, bạn có thể thêm một phương thức vào ApiContoder của bạn (hoặc tốt hơn là bộ điều khiển cơ sở của bạn) như thế này:

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

Sau đó, bạn có thể trả về nó giống như bất kỳ phương thức được xây dựng nào:

[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

Một bản cập nhật cho câu trả lời @Aliostads bằng cách sử dụng nhiều moden được IHttpActionResultgiới thiệu trong API Web 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);
    }
}

0

Thử cái này :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.