从Web Api控制器返回http状态码
我试图返回一个状态代码304没有修改的GET方法在一个Web API控制器。
我成功的唯一方法是这样的:
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; } }
这里的问题是它不是一个例外,它只是没有修改,所以客户端caching是确定的。 我也希望返回types是一个用户(因为所有的web api示例与GET显示)不返回HttpResponseMessage或类似的东西。
我不知道答案,所以问了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); }
如果您想保留动作签名作为返回用户,您也可以执行以下操作:
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; }
另外一个select:
return new NotModified();
public class NotModified : IHttpActionResult { public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { var response = new HttpResponseMessage(HttpStatusCode.NotModified); return Task.FromResult(response); } }
我不喜欢不得不改变我的签名来使用HttpCreateResponsetypes,所以我想出了一些扩展的解决scheme来隐藏它。
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." }); }
如果你需要返回一个IHttpActionResult并且想要返回错误代码加上一条消息,使用:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
我讨厌碰撞旧文章,但这是谷歌search的第一个结果,我有一个这个问题(即使在你们的支持)的时间。 所以这里一无所有
希望我的解决scheme能够帮助那些也感到困惑的人。
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; } } }
然后从BaseControllerinheritance
[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); }