245

我正在尝试为 Web api 控制器中的 GET 方法返回未修改的状态代码 304。

我成功的唯一方法是这样的:

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

这里的问题是它不是一个例外,它只是没有被修改所以客户端缓存是好的。我还希望返回类型是用户(正如所有 web api 示例都使用 GET 所示)而不返回 HttpResponseMessage 或类似的东西。

4

14 回答 14

271

我不知道答案,所以在这里问了 ASP.NET 团队。

所以诀窍是将签名更改为HttpResponseMessage并使用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);
}
于 2012-05-18T19:25:14.750 回答
68

如果要将操作签名保留为返回用户,还可以执行以下操作:

public User GetUser(int userId, DateTime lastModifiedAtClient) 

如果您想返回其他内容,200那么您将在您的操作中抛出一个HttpResponseException并传递HttpResponseMessage您想要发送给客户端的内容。

于 2012-06-15T20:41:04.023 回答
42

更改 GetXxx API 方法以返回 HttpResponseMessage,然后返回完整响应的类型化版本和 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);
        }
    }

*ClientHasStale 数据是我检查 ETag 和 IfModifiedSince 标头的扩展。

MVC 框架仍应序列化并返回您的对象。

笔记

我认为通用版本将在 Web API 的某些未来版本中被删除。

于 2012-07-04T11:31:51.810 回答
41

在 MVC 5 中,事情变得更容易了:

return new StatusCodeResult(HttpStatusCode.NotModified, this);
于 2015-04-01T10:57:31.893 回答
17

我讨厌撞旧文章,但这是谷歌搜索中的第一个结果,我有一段时间解决这个问题(即使在你们的支持下)。所以这里什么都没有...

希望我的解决方案能帮助那些也感到困惑的人。

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

然后从 BaseController 继承

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

然后使用它

[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);
}
于 2017-08-15T15:38:01.820 回答
16

对于 ASP.NET Web Api 2,MS 的这篇文章建议将方法的返回类型更改为IHttpActionResult. 然后,您可以返回一个内置的IHttpActionResult实现,如Ok,BadRequest等(参见此处)或返回您自己的实现。

对于您的代码,可以这样做:

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);
}
于 2019-04-26T18:39:44.197 回答
14

.net core 2.2 返回 304 状态码。这是使用 ApiController。

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

您可以选择返回一个带有响应的对象

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304, YOUROBJECT); 
    }
于 2019-01-02T11:10:22.127 回答
5

我不喜欢更改我的签名来使用 HttpCreateResponse 类型,所以我想出了一些扩展的解决方案来隐藏它。

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

然后,您可以向您的 ApiController(或更好的基本控制器)添加一个方法,如下所示:

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

然后你可以像任何内置方法一样返回它:

[HttpPost]
public IHttpActionResult Post(Model model)
{
    return model.Id == 1 ?
                Ok() :
                CustomResult(HttpStatusCode.NotAcceptable, new { 
                    data = model, 
                    error = "The ID needs to be 1." 
                });
}
于 2017-01-26T20:01:18.060 回答
4

另外一个选项:

return new NotModified();

public class NotModified : IHttpActionResult
{
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotModified);
        return Task.FromResult(response);
    }
}
于 2016-11-15T13:56:13.013 回答
4

试试这个 :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
于 2019-10-04T19:28:11.870 回答
3

如果您需要返回 IHttpActionResult 并希望返回错误代码和消息,请使用:

return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
于 2016-07-29T05:55:11.307 回答
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;
}
于 2015-07-08T21:32:56.330 回答
2

IHttpActionResult使用Web API 2 中引入的更现代的更新 @Aliostads 答案。

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);
    }
}
于 2019-04-24T09:36:57.973 回答
0

我知道这里有几个很好的答案,但这是我需要的,所以我想我会添加此代码,以防其他人需要使用 webAPI 在 4.7.x 中返回他们想要的任何状态代码和响应正文。

public class DuplicateResponseResult<TResponse> : IHttpActionResult
{
    private TResponse _response;
    private HttpStatusCode _statusCode;
    private HttpRequestMessage _httpRequestMessage;
    public DuplicateResponseResult(HttpRequestMessage httpRequestMessage, TResponse response, HttpStatusCode statusCode)
    {
        _httpRequestMessage = httpRequestMessage;
        _response = response;
        _statusCode = statusCode;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(_statusCode);
        return Task.FromResult(_httpRequestMessage.CreateResponse(_statusCode, _response));
    }
}
于 2021-01-04T21:14:22.010 回答