相关:Modify static file response in ASP.NET Core
但是,当我的业务逻辑抛出我的一个自定义异常,例如UnprocessableException
时,我不明白为什么以下代码有效:
try
{
await next.Invoke(context);
}
catch (UnprocessableException uex)
{
Logger.Warn(uex);
context.Response.StatusCode = 422;
var responseContent = JsonConvert.SerializeObject(new { uex.Message });
await context.Response.WriteAsync(responseContent);
}
// more specific exceptions resulting in HTTP 4xx status
但是当链中的最后一个IndexOutOfRangeException
块捕获到完全意外的catch
时
catch (Exception ex)
{
Logger.Error(ex);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var responseContent = env.IsDevelopment()
? JsonConvert.SerializeObject(new { ex.Message, ex.StackTrace })
: JsonConvert.SerializeObject(new { Message = "An internal error occured" });
await context.Response.WriteAsync(responseContent);
}
尝试设置状态代码时抛出此异常:
System.InvalidOperationException: StatusCode cannot be set, response has already started.
bei Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.ThrowResponseAlreadyStartedException(String value)
bei Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.set_StatusCode(Int32 value)
bei Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 value)
bei Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_StatusCode(Int32 value)
bei Anicors.Infrastructure.Middlewares.ScopeMiddleware.<Invoke>d__5.MoveNext()
答案 0 :(得分:3)
这里只是个问题:我从处理WebSocket连接的控制器收到此错误。当关闭WebSocket连接(用户关闭浏览器选项卡)时,抛出此异常:System.InvalidOperationException: StatusCode cannot be set because the response has already started.
还请注意,在堆栈跟踪中找不到用于处理WebSocket连接的控制器:
System.InvalidOperationException: StatusCode cannot be set because the response has already started.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(String value)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.set_StatusCode(Int32 value)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 value)
at Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_StatusCode(Int32 value)
at Microsoft.AspNetCore.Mvc.StatusCodeResult.ExecuteResult(ActionContext context)
at Microsoft.AspNetCore.Mvc.ActionResult.ExecuteResultAsync(ActionContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsync[TFilter,TFilterAsync]()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context)
at MyApp.Middleware.MyAppNotFoundHandlerMiddleware.Invoke(HttpContext context) in C:\Proj\MyApp\Middleware\MyAppNotFoundHandlerMiddleware.cs:line 24
at MyApp.Middleware.MyAppExceptionHandlerMiddleware.Invoke(HttpContext context) in C:\Proj\MyApp\Middleware\MyAppExceptionHandlerMiddleware.cs:line 26
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
这是控制器操作出错的地方:
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
var socket = await HttpContext.WebSockets.AcceptWebSocketAsync();
Clients.Add(socket);
await WaitForClose(HttpContext, socket);
}
return Ok();
}
正如其他答案所述,罪魁祸首是return Ok()
。该语句在套接字关闭时执行,但是到那时,HTTP连接早已关闭。
我正在使用2.1.0版的NuGet软件包Microsoft.AspNetCore.WebSockets
。
答案 1 :(得分:2)
哦,好吧,我正在进一步调查,同时试图重现案件更加孤立,我找到了根本原因。
但首先是一些历史:我在生产中看到了这些错误,但从未能够重现它。现在我正在开发另一个功能,由于我的开发机器上的数据库结构出错,这个错误发生在使用正常连接查询的每个请求上。所以我想,嘿,这是解决这个问题的时刻......但最终还是来了。
然而,为了更多地隔离它,我做了一个动作只是在我脸上扔了NotImplementedException
。并猜测:它按预期工作。 HTTP 500,没有“无法设置StatusCode,响应已经开始”。
有什么区别?不同的是,我的另一个失败的控制器返回:
IQueryable<MySearchRecords> searchResult = service.Search(/*snipped boring stuff*/);
var result = DataSourceLoader.Load(searchResult, loadOptions);
return Ok(result);
而DataSourceLoader是一个.net类,支持DevExpress的DevExtreme JS Framework。事实证明,结果是object
,因为它返回一个普通数组或一个包含类型,它也提供了一些元数据(例如,用于分页和填充)。在我的情况下,它会应用一些Take
和Skip
,但是:不会枚举搜索结果,但会返回IQueryable<>
!因此,枚举不会早于将结果呈现给JSON时完成。这就是我在这种特殊情况下看到InvalidOperationException的原因,但不是直接从控制器抛出它的原因。
尽管如此,它表明我的异常处理在所有情况下都没有按预期工作。我已经读过你可以替换整个响应流来避免这个问题,但这有一些缺点。那么处理这种情况的正确方法是什么呢?无论如何,我想要使用自定义JSON内容的HTTP 500。
答案 2 :(得分:2)
由于这是Google上搜索量最高的结果,因此我不妨告诉新手如何提出此错误。
我试图通过压缩文件并将其下载(流式传输)到客户端来使用this答案。我在实际的控制器操作结束时返回了return Ok()
。我需要退回return new EmptyResult()
答案 3 :(得分:0)
就我而言,我试图打印到控制台上以调试某些内容,并从互联网上的某个地方找到Response.WriteAsync();
。我将其粘贴到方法的顶部,这就是为什么我遇到错误"System.InvalidOperationException: StatusCode cannot be set because the response has already started."
删除Response.WriteAsync("test")
方法解决了我的问题!最简单的事情__(ツ)_ //
感谢CularBytes对return new EmptyResult()
的建议,该建议打印了我的WriteAsync("test")
并暴露了我的错误。
答案 4 :(得分:0)
我的自定义中间件抛出了此错误,但是您可以通过检查它来检查“响应是否已经开始”:
if (!context.Response.HasStarted)
{ ... }
完整代码:
private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
if (!context.Response.HasStarted)
{
string result;
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
result = JsonConvert.SerializeObject(new { error = "An error has occured" });
_logger.LogError(ex, CreateErrorMessage(context));
context.Response.ContentType = "application/json";
return context.Response.WriteAsync(result);
}
else
{
return context.Response.WriteAsync(string.Empty);
}
}
答案 5 :(得分:0)
解决方案非常简单。一旦编写了响应,就不应将上下文传递给请求委托。如下例所示。
Select col/power(10,(length(trunc(col))-1))
From your_table
Where col >= 1000
确保RequestDelegate不会在响应状态中获取上下文。