如何解决Web API发送HTTP Headers后触发的状态错误?
解决ASP.NET API返回文件后触发的HTTP Headers已发送错误
问题背景
我有一个通过HttpGet调用的API,逻辑如下:
- 请求验证通过:直接返回Excel或CSV格式的报告文件
- 请求验证失败:返回包含错误码和信息的Json Result对象
API调用可成功在浏览器下载文件,但全局应用错误处理器会触发如下错误:
System.Web.HttpException (0x80004005): Server cannot set status after HTTP headers have been sent. at System.Web.HttpResponse.set_StatusCode(Int32 value) at System.Web.Http.WebHost.HttpControllerHandler.<CopyResponseStatusAndHeadersAsync>d__31.MoveNext()
推测是函数在发送HTTP Headers后仍试图返回Nothing,导致Web API框架后续尝试修改响应头引发冲突。
相关代码
API控制器方法
<HttpGet> Public Function AdvReport(APIKey As String, SubID As String) As Object Dim lRes As New Result Try ' Check Key If APIKey Is Nothing OrElse APIKey.Length = 0 Then Return lRes.Init(Result.ResultCode.NoAction, "API Key is missing") lRes = APIKeyClass.APIHit(APIKey) If lRes.NoSuccess Then Return lRes ' Get RepID If String.IsNullOrEmpty(SubID) Then Return lRes.Init(Result.ResultCode.NoAction, "SubID is missing") SubID = Encrypter.Decrypt(SubID) If SubID.Length = 0 OrElse Not IsNumeric(SubID) Then Return lRes.Init(Result.ResultCode.NoAction, "Invalid SubID") ' Load Advanced Report Subscription Dim lSub As New AdvRepSubClass lRes = lSub.Load(CInt(SubID)) If lRes.NoSuccess Then Return lRes If Not lSub.API Then Return lRes.Init(Result.ResultCode.NoAction, "Report not enabled for API access") ' Generate report If lRes.IsSuccess Then lRes = lSub.GetReportBin(Nothing, False) If lRes.IsSuccess Then LogClass.Log(Nothing, "API AdvReport", "Report Name: " + lSub.Title + "; Result: " + If(lRes.IsSuccess, "Success; Rows Affected: " + lRes.RowsAffected.ToString, lRes.Info) + "; API Key: " + APIKey, LogClass.LogTable.AdvRep, lSub.AdvRepID) ' Download it Try DownloadByteArray(CType(lRes.RetInfo, Byte()), GetReportFileName(lSub.Title, lSub.RepType), False) Return Nothing Catch ex As Exception lRes.Init(Result.ResultCode.Err, ex.Message) End Try Return lRes Catch ex As Exception Return lRes.Init(Result.ResultCode.Err, ex.Message) End Try End Function
DownloadByteArray方法
Public Sub DownloadByteArray(aContent As Byte(), aFileName As String, aPreview As Boolean) Try HttpContext.Current.Response.Buffer = False HttpContext.Current.Response.Clear() HttpContext.Current.Response.AddHeader("Content-Disposition", If(aPreview, "inline", "attachment") + "; filename=""" & aFileName & """") If aPreview Then HttpContext.Current.Response.ContentType = Helper.GetContentType(aFileName) Else HttpContext.Current.Response.ContentType = "application/octet-stream" HttpContext.Current.Response.AddHeader("Content-Length", aContent.Length.ToString()) HttpContext.Current.Response.BinaryWrite(aContent) HttpContext.Current.Response.Flush() HttpContext.Current.Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest() Catch ex As Exception Dim lUserID As String = If(HttpContext.Current.Session IsNot Nothing AndAlso HttpContext.Current.Session("UserID") IsNot Nothing, HttpContext.Current.Session("UserID").ToString, Nothing) LogClass.Log(Nothing, lUserID, "DownloadByteArray", String.Concat("Doc: ", aFileName, "; Ex: ", ex.ToString())) End Try End Sub
解决方案
核心问题是:直接操作HttpContext.Current.Response发送文件后,Web API框架仍会尝试处理控制器方法的返回值(哪怕是Nothing),导致框架试图修改已发送的HTTP头,触发错误。
具体修改
修改API方法中的文件下载逻辑,在成功发送文件后直接终止请求流程,不让Web API框架继续处理返回值:
' Download it Try DownloadByteArray(CType(lRes.RetInfo, Byte()), GetReportFileName(lSub.Title, lSub.RepType), False) ' 通知ASP.NET完成当前请求处理,终止后续流程 HttpContext.Current.ApplicationInstance.CompleteRequest() ' 直接退出方法,避免执行后续Return语句 Exit Function Catch ex As Exception lRes.Init(Result.ResultCode.Err, ex.Message) End Try
关键说明
- 不要使用
Response.End():它会抛出ThreadAbortException,可能引发额外异常 ApplicationInstance.CompleteRequest():让ASP.NET正常完成当前请求的生命周期,不会中断线程,同时阻止Web API框架后续处理响应头Exit Function:确保控制器方法不再返回任何值,彻底终止方法执行
修改后,Web API框架不会再尝试修改已发送的HTTP头,即可消除该错误。
内容的提问来源于stack exchange,提问作者navigator
相关产品推荐
相关产品推荐

