在使用Owin自托管时,从WebApi控制器抛出HttpResponseException。

15
我们正在构建一个使用Owin托管的WebApi。以前,我们在控制器操作中使用HttpResponseException来返回404状态码等,并且一直运行良好。
然而,当我们开始使用Owin(自托管)时,我们遇到了这个问题,即HttpResponseException被序列化为json/xml,状态码从404变为500(内部服务器错误)。以下是我们的代码:
public class InvoicesController : ApiController
{
    private readonly IInvoiceRepository _invoiceRepository;

    public InvoicesController(IInvoiceRepository invoiceRepository)
    {
        _invoiceRepository = invoiceRepository;
    }

    [HttpGet]
    public IEnumerable<AccountCodeAssignment> AssignAccountCodesToInvoiceById(int id)
    {
        var invoice = _invoiceRepository.Get(id);

        if (invoice == null) throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, "Invoice not found"));

        yield return new AccountCodeAssignment(1, ...);
        yield return new AccountCodeAssignment(2, ...);
        yield return new AccountCodeAssignment(3, ...);
        yield return new AccountCodeAssignment(4, ...);
    }
}

我们收到的响应是一个500错误代码:

{
    "Message": "An error has occurred.",
    "ExceptionMessage": "Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.",
    "ExceptionType": "System.Web.Http.HttpResponseException",
    "StackTrace": "   at AccountCodeAssignmentService.Controllers.InvoicesController.<AssignAccountCodesToInvoiceById>d__0.MoveNext() in c:\\Projects\\AccountCodeAssignmentService\\Source\\AccountCodeAssignmentService\\Controllers\\InvoicesController.cs:line 38\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n   at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n   at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n   at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n   at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)\r\n   at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__13.MoveNext()"
}

我们到底做错了什么或者说在使用Owin自托管时HttpResponseException不支持吗?

编辑: 对我们来说,使用WebApi的一个巨大优势是可以处理和返回我们自己的类型,所以我们希望避免改变返回类型。目前我们正在生成AccountCodeAssignment,因此更改返回类型不是一个选项。

5个回答

27

我在使用Postman测试Web API时遇到了这个问题,请求类型设置为纯文本而不是application/json。


三年后,你的回答指引了我正确的方向。由于我忘记删除另一个请求的标头,导致了相同的问题,谢谢你 :) - Ibrahim Mezouar

4

您可以在OnException()中的异常处设置断点,并查看context.Exception.Response,以查看“原因短语”并得到解释。

您还可以通过以下方式访问它:

((System.Web.Http.HttpResponseException)context.Exception).Response.ReasonPhrase

对我来说,出现了

不支持的媒体类型

其他人已经提到,当您进行文本请求时可能会发生此问题,因为默认情况下不处理文本请求。如果确实想允许文本,请参考: 如何将纯文本发布到ASP.NET Web API端点?


4
我认为问题不在于抛出HttpResponseException。如果您查看您发布的堆栈跟踪,问题似乎出现在对MoveNext()的调用中。这是您所拥有的yield语句的内部C#表示。
我可能错了,但验证这一点最简单的方法是在第一个yield语句上设置断点并查看它是否会命中。我的猜测是它会,即它不会抛出HttpResponseException。此外,只需暂时更改代码始终抛出HttpResponseException并查看它如何处理即可。
我目前正在使用OWIN自托管的项目上工作,我可以抛出HttpResponseException而没有任何问题。
另外,您可能需要调查全局异常处理。我发现将所有异常处理集中在一个地方非常有用。请注意,HttpResponseException是一种特殊情况,不受全局异常处理程序处理。

是的,这个问题似乎与“yield”有关。如果我们将产生的行提取到一个返回IEnumberable<AccountCodeAssignment>的方法中,并在操作中返回该方法的结果,那么它就可以工作了!所以我猜原因已经找到了,但我仍然不知道为什么会发生这种情况!总之,不要在同一个方法中使用yield和HttpResponseException! - Kristoffer Ahl
我无法设置任何断点,因为本地针对生产环境进行目标定位时,我无法复制。有时我只能在生产日志中看到一些错误信息。 - Dalibor

2

对于我来说,在我的API头中缺少了content-type。在将content-type添加为application/json后,这个问题就得到了解决。这可能会帮助其他人解决类似问题。


1

我不完全确定,但可能是因为您的操作返回了IEnumerable<>。

尝试将其更改为IHttpActionResult。

请尝试此操作。

  [ResponseType(typeof(AccountCodeAssignment))]
         public IHttpActionResult AssignAccountCodesToInvoiceById (int id)
        {

         var invoice = _invoiceRepository.Get(id);

                if (invoice == null) {

                return NotFound();
         }



               return Ok(invoice);
            }

2
这种方法可能有效,但我们想要使用并返回我们自己的类型。对于我们来说,使用WebApi的一个重要优势就是能够做到这一点,所以我们希望避免改变返回类型。目前我们正在产生AccountCodeAssignment,所以这种方法对我们来说不适用。 - Kristoffer Ahl
您可以添加 ResponseType 属性,请测试,看看是否有效。 - sylwester
关键在于我们正在产生多个AccountCodeAssignment(请参见更新的示例),因此如果我们更改返回类型,编译器将会报错!ResponseType属性无法解决此问题,我也不指望它能够解决。 - Kristoffer Ahl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接