我想包装IHttpActionResult,因为我需要一些额外的数据供客户端应用程序使用。
我的第一个方法是创建并返回简单的DTO,如果成功则包装结果对象:
回复DTO:
public class Response<T>
{
public string ErrorMessage { get; set; }
public bool Success { get; set; }
public string CodeStatus { get; set; }
public T Result { get; set; }
public Response(bool isSuccess, [Optional] T result, [Optional] string codeStatus, [Optional] string errorMessage)
{
Success = isSuccess;
Result = result;
CodeStatus = codeStatus;
ErrorMessage = errorMessage;
}
}
控制器:
public IHttpActionResult Get(int id)
{
return BadRequest(new Response<MyObjectClass>(false, null,"Invalid Id",400));
...
return Ok(new Response<MyObjectClass>(true, result);
}
我发现处理包装非常无效。我觉得它不是很优雅。我试图找出一些通用的解决方案,最后得到以下结论:
示例控制器操作:
public IHttpActionResult GetById(int id)
{
var result = _someService.Get(id);
if (result == null)
return NotFound().WithError("Invalid Id");
return Ok().WithSuccess(result);
}
这仍然会返回Response DTO。
我已经包装了IHttpActionResult来处理创建Response DTO:
public class HttpActionResult : IHttpActionResult
{
private readonly string _errorMessage;
private readonly IHttpActionResult _innerResult;
private readonly object _result;
private readonly bool _isSuccess;
public HttpActionResult(IHttpActionResult inner, bool isSuccess, object result,string errorMessage)
{
_errorMessage = errorMessage;
_innerResult = inner;
_result = result;
_isSuccess = isSuccess;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await _innerResult.ExecuteAsync(cancellationToken);
response.Content = new ObjectContent(typeof(Response), new Response(_isSuccess, _result, ((int)response.StatusCode).ToString(), _errorMessage), new JsonMediaTypeFormatter());
return response;
}
}
最后,我为IHttpActionResult添加了扩展方法,以便在控制器中使用:
public static class IHttpActionResultExtensions
{
public static IHttpActionResult WithSuccess(this IHttpActionResult inner, object result = null, string message = null)
{
return new HttpActionResult(inner, true, result, message);
}
public static IHttpActionResult WithError(this IHttpActionResult inner, string message = null)
{
return new HttpActionResult(inner, false,null, message);
}
}
在API控制器中处理包装http消息有哪些替代方法? 您在我的解决方案中看到了哪些弱点?
答案 0 :(得分:2)
WebAPI旨在用于创建RESTful Web服务。您为什么要提供另一层状态和其他详细信息? HTTP足以满足这些要求。例如,您可以使用标准状态代码和子代码,如下所示:500.1
,500.2
。
使用HTTP状态代码更容易表达成功或失败。 2XX
成功操作的范围,以及您可以使用的不成功操作,例如400
(错误请求)。 401
用于未经授权的访问... 500
表示服务器发生故障...
WebAPI已经提供ModelState
让框架构建响应对象。使用它并尝试不重新发明轮子。
再次,保持简单。响应实体继续响应。成功或失败由状态代码表示。有关错误请求的详细信息将添加到ModelState
字典中。应将错误消息设置为响应ReasonPhrase
。
IHttpActionResult
实现旨在将您的域结果转换为HTTP响应。也就是说,除非您尝试按方式返回响应对象,否则您将处于正确的轨道中。我建议您应该使用IHttpActionResult
将自己的响应对象的每个细节设置为标准HTTP语义,并使用ModelState
开箱即用的方法通知错误。
答案 1 :(得分:1)
避免使用IHttpActionResult
并将HttpResponseException
与业务实体一起用作结果类型。在您的解决方案中,您无法编写静态类型的测试用例。
例如,
protected void ThrowHttpError(HttpStatusCode statusCode, string message)
{
throw new HttpResponseException(
new HttpResponseMessage(statusCode) {
ReasonPhrase = message,
// HTTP 2.0 ignores ReasonPhrase
// so we send ReasonPhrase again in the Content
Content = new StringContent(message)
});
}
// some generic option...
protected void ThrowHttpError<T>(HttpStatusCode statusCode, T content)
where T:class
{
throw new HttpResponseException(
new HttpResponseMessage(statusCode) {
ReasonPhrase = "Error",
Content = JsonConvert.Serialize(content)
});
}
你的方法,
public async Task<Product> Get(long id){
var product = await context.Products
.FirstOrDefaultAsync( x=> x.ProductID == id);
if(product==null){
ThrowHttpError(HttpStatusCode.NotFound,
$"Product not found for {id}");
}
if(product.RequiresValidation){
// generic version....
ThrowHttpError(HttpStatusCode.Conflict,
new Product{
ProductID = product.ProductID,
ValidationRequestCode = product.ValidationRequestCode
});
}
return product;
}
更多信息,您可以自定义方法ThrowHttpError以满足您的需求。最好的部分是,它仍然是可测试的。