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

ozb*_*zba 213 c# httpresponse http-status-codes asp.net-web-api

我正在尝试在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是它不是一个例外,它只是没有修改,所以客户端缓存是好的.我还希望返回类型是User(因为所有web api示例都显示为GET)不返回HttpResponseMessage或类似的东西.

Ali*_*tad 243

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

  • 要添加到niico的建议,当返回类型为`IHttpActionResult`并且您想要返回User时,您可以执行`return Ok(user)`.如果你需要返回另一个状态代码(例如,禁止),你可以做`return this.StatusCode(HttpStatusCode.Forbidden)`. (8认同)
  • 如果有人需要它,从控制器方法获取值将是`GetUser(request,id,lastModified).TryGetContentValue(out user)`,其中`user`(在示例中)是一个`User`对象. (5认同)
  • 这仍然是2015年的首选方法吗?MVC 5? (4认同)
  • 更现代的版本返回IHttpActionResult - 而不是HttpResponseMessage(2017) (4认同)
  • 它不能在ASP.NET MVC 4 beta版本中编译,因为CreateResponse仅将状态代码作为参数.其次我想要一个没有HttpResponseMessage的解决方案作为返回值,因为它已被弃用:http://aspnetwebstack.codeplex.com/discussions/350492 (3认同)

小智 68

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

public User GetUser(int userId, DateTime lastModifiedAtClient) 
Run Code Online (Sandbox Code Playgroud)

如果您想要返回除此之外的其他内容,200您可以HttpResponseException在您的操作中投入并传入HttpResponseMessage您想要发送给客户端的内容.

  • 是的,如果您正在设计一个繁忙的API,使用异常来传达最常见的"NotModified"案例真的很浪费.如果您的所有API都这样做,那么您的服务器将主要将瓦特转换为异常. (10认同)
  • 这是一种更优雅的解决方案(albiet不完整的答案).为什么每个人都喜欢这么做? (9认同)
  • @Geoist http://stackoverflow.com/questions/1282252/how-much-more-expensive-is-an-exception-than-a-return-value.抛出异常是昂贵的. (4认同)
  • @nagytech因为如果你抛出一个错误(比如400响应)就无法返回自定义错误消息...同样抛出异常对你希望代码做的事情来说是愚蠢的.昂贵的,当你不必要的时候会被记录下来.他们并不是真的例外. (2认同)

Luk*_*ett 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

注意

我认为在未来的Web API版本中将删除通用版本.

  • 这是我正在寻找的确切答案 - 尽管是一个Task <HttpResponseMessage <T >>返回类型.谢谢! (4认同)

Jon*_*tes 38

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

return new StatusCodeResult(HttpStatusCode.NotModified, this);
Run Code Online (Sandbox Code Playgroud)

  • 无法指定信息? (2认同)

dat*_*ung 15

对于 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);
}
Run Code Online (Sandbox Code Playgroud)


Ken*_*rza 13

我讨厌碰到旧文章,但这是谷歌搜索中的第一个结果,我对这个问题有一点时间(即使有你们的支持).所以没有什么......

希望我的解决方案能够帮助那些也很困惑的人.

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后只从BaseController继承

[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...
Run Code Online (Sandbox Code Playgroud)

然后使用它

[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);
}
Run Code Online (Sandbox Code Playgroud)


don*_*ega 7

尝试这个 :

return new ContentResult() { 
    StatusCode = 404, 
    Content = "Not found" 
};
Run Code Online (Sandbox Code Playgroud)


Ive*_*.me 6

.net core 2.2 returning 304 status code. This is using an ApiController.

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304);
    }
Run Code Online (Sandbox Code Playgroud)

Optionally you can return an object with the response

    [HttpGet]
    public ActionResult<YOUROBJECT> Get()
    {
        return StatusCode(304, YOUROBJECT); 
    }
Run Code Online (Sandbox Code Playgroud)


kri*_*gar 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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