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

我试图返回一个状态代码304未修改的web 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;
}
}
这里的问题是它不是一个异常,它只是没有被修改,所以客户端缓存是OK的。 我还想返回类型是一个用户(所有的web api的例子显示与GET)不返回HttpResponseMessage或类似的东西
410138 次浏览

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

如果您希望将操作签名保留为返回User,还可以执行以下操作:

public User GetUser(int userId, DateTime lastModifiedAtClient)

如果你想返回200以外的东西,那么你在动作中抛出一个HttpResponseException,并传递你想要发送给客户端的HttpResponseMessage

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

在MVC 5中,事情变得更简单了:

return new StatusCodeResult(HttpStatusCode.NotModified, this);
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;
}

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

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

另一个选择:

return new NotModified();

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

我不喜欢改变我的签名来使用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."
});
}

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

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

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

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

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

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

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

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

https://learn.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);
}
}

ASP。NET Web Api 2, 这篇文章来自微软建议将方法的返回类型更改为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);
}

试试这个:

return new ContentResult() {
StatusCode = 404,
Content = "Not found"
};

我知道这里有几个很好的答案,但这是我需要的,所以我想我应该添加这个代码,以防任何人需要返回任何状态代码和响应体,他们想在4.7。x与webAPI。

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