I have an API called using HttpGet. The API validates the call and returns a report file directly (Excel or CSV). If the validation fails, it returns a Json Result
object with an error code and message.
This is the API function:
<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
This is the code for 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
The code runs fine and the file is downloaded successfully on the browser using the API call. The issue is that the global application error handler is also fired after the API call with this error:
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()
I understand what the error means. I assume the function is trying to set a return value to Nothing after the Http headers have been sent. How do I resolve this and somehow "stop" the function after the file has been sent?
I managed to solve the issue by not calling the common download function and writing the file return inline only.
' If file required, download it
Try
Dim lResult As New HttpResponseMessage(HttpStatusCode.OK)
lResult.Content = New ByteArrayContent(CType(lRes.RetInfo, Byte()))
lResult.Content.Headers.ContentDisposition = New ContentDispositionHeaderValue("attachment") With {
.FileName = GetReportFileName(lSub.Title, lSub.RepType)
}
lResult.Content.Headers.ContentType = New MediaTypeHeaderValue("application/octet-stream")
Return lResult
Catch ex As Exception
lRes.Init(Result.ResultCode.Err, ex.Message)
End Try