从Web Api控制器返回http状态代码


219

我试图返回状态304,但未在网络api控制器中为GET方法修改。

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

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

这里的问题是,这不是一个例外,它没有被修改,因此客户端缓存正常。我还希望返回类型为User(如所有Web API示例使用GET所示)不返回HttpResponseMessage或类似的内容。


您正在使用beta还是每晚构建
Aliostad

@Aliostad我正在使用Beta
ozba,2012年

那么返回到底有什么问题new HttpResponseMessage(HttpStatusCode.NotModified)呢?不行吗
Aliostad

@Aliostad当返回类型为User时,我无法返回HttpResponseMessage,它没有进行编译(显然)。
奥兹巴

Answers:


251

我不知道答案,所以在这里问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);
}

3
它不能在ASP.NET MVC 4 beta版本中进行编译,因为CreateResponse仅将状态代码作为参数。其次,我想要一个不使用HttpResponseMessage作为返回值的解决方案,因为它已经过时:aspnetwebstack.codeplex.com/discussions/350492
ozba 2012年

5
如果有人需要,从controller方法获取值将是GetUser(request, id, lastModified).TryGetContentValue(out user),其中user(在示例中)是一个User对象。
Grinn

4
这仍然是2015年的首选方法吗?MVC 5?
暗恋

4
更现代的版本返回IHttpActionResult-不是HttpResponseMessage(2017)
niico

8
要添加到niico的建议中,当返回类型为IHttpActionResult并且您想返回User时,只需执行即可return Ok(user)。如果您需要返回其他状态代码(例如,禁止的),则可以这样做return this.StatusCode(HttpStatusCode.Forbidden)
画了

68

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

public User GetUser(int userId, DateTime lastModifiedAtClient) 

如果您想返回其他内容,200则可以执行一个HttpResponseException操作,然后将HttpResponseMessage您要发送的内容传递给客户端。


9
这是一种更优雅的解决方案(答案不完整)。为什么每个人都喜欢用艰辛的方式做到这一点?
nagytech

4
@Geoist stackoverflow.com/questions/1282252/...。抛出异常的代价很高。
tia

10
是的,如果您正在设计繁忙的API,那么使用异常来传达最常见的情况NotModified确实很浪费。如果您所有的API都这样做,那么您的服务器将主要将瓦转换为异常。
路加·普普利特

2
@nagytech,因为如果抛出错误(例如400响应),您将无法返回自定义错误消息...对于期望代码执行的操作,抛出异常也很愚蠢。昂贵,当您不需要它们时会记录下来。他们并不是真正的例外。
罗克兰

40

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

return new StatusCodeResult(HttpStatusCode.NotModified, this);

3
无法指定信息?
暗恋

1
使用消息实际上是公认的答案。这只是一个小麻烦
Jon Bates

39

更改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版本中删除。


4
这是我一直在寻找的确切答案-尽管它是Task <HttpResponseMessage <T >>返回类型。谢谢!
xeb 2012年

1
@xeb-是的,这完全值得一提。有关异步的更多信息,请 参见asp.net/mvc/tutorials/mvc-4/…–
卢克·普普利特

14

我讨厌碰碰旧文章,但这是google搜索的第一个结果,而且我在这个问题上花了很长时间(即使在你们的支持下)。所以这里什么都没有...

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

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

1
辉煌。节省了我很多时间。
gls123'4

10

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

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

(可选)您可以返回带有响应的对象

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

7

对于ASP.NET Web Api 2,MS的这篇文章建议将方法的返回类型更改为IHttpActionResult。然后,您可以返回一个内置的IHttpActionResult实现一样OkBadRequest等(见这里),还是返回自己的实现。

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

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

另外的选择:

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

如果您需要返回一个IHttpActionResult并想返回错误代码和一条消息,请使用:

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

2

我不想更改自己的签名以使用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." 
                });
}

0

@Aliostads的更新使用IHttpActionResultWeb API 2中引入的更现代的方式回答。

https://docs.microsoft.com/zh-cn/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

试试这个 :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.