我正在尝试为 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 或类似的东西。
beta
还是 nightly build?
new HttpResponseMessage(HttpStatusCode.NotModified)
有什么问题?它不工作吗?
我不知道答案,所以问 ASP.NET 团队 here。
所以诀窍是将签名更改为 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
。
NotModified
情况真的很浪费。如果您的所有 API 都这样做,那么您的服务器将主要将瓦特转换为异常。
更改 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);
对于 ASP.NET Web Api 2,this post from MS 建议将方法的返回类型更改为 IHttpActionResult
。然后,您可以返回内置的 IHttpActionResult
实现,如 Ok
、BadRequest
等 (see here) 或返回您自己的实现。
对于您的代码,可以这样做:
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);
}
我讨厌撞旧文章,但这是谷歌搜索中的第一个结果,我有一段时间解决这个问题(即使在你们的支持下)。所以这里什么都没有...
希望我的解决方案能帮助那些也感到困惑的人。
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);
}
我不喜欢更改我的签名来使用 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."
});
}
HttpStatusCode.BadRequest
(400)。 HttpStatusCode.NotAcceptable
(406) 在内容协商/接受标头指定服务器不接受的内容时使用。
尝试这个 :
return new ContentResult() {
StatusCode = 404,
Content = "Not found"
};
如果您需要返回 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);
}
}
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;
}
使用 Web API 2 中引入的更现代的 IHttpActionResult
更新 @Aliostads 答案。
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);
}
}
我知道这里有几个很好的答案,但这是我需要的,所以我想我会添加此代码,以防其他人需要使用 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));
}
}
GetUser(request, id, lastModified).TryGetContentValue(out user)
,其中user
(在示例中)是一个User
对象。IHttpActionResult
并且您想要返回 User 时,您只需执行return Ok(user)
。如果您需要返回另一个状态码(例如,禁止),您只需执行return this.StatusCode(HttpStatusCode.Forbidden)
。