bzamfir
bzamfir

Reputation: 4906

Downloading file with webapi

I'm trying to write an action in a webapi controllers to allow downloading a file. But for some strange reason, the code doesn't works.

Here is my code:

<RoutePrefix("api/files")>
Public Class PermitFilesController
    Inherits ApiController

    <Route("download")>
    public function GetFile() As HttpResponseMessage 

        Dim fStream as FileStream =  File.Open("C:\Projects\1234.pdf", FileMode.Open, FileAccess.Read )
        Dim response = Request.CreateResponse(HttpStatusCode.OK)
        response.Content = new StreamContent(fStream)
        'response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        'response.Content.Headers.ContentDisposition.FileName = Path.GetFileName(fStream.Name)
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf")
        return response
    End Function

I try to download simply using the url in browser:

localhost:<myport>/api/files/download

The error (in Chrome ) is Error code: ERR_CONNECTION_RESET In FF, it is even stranger: it redirects me to www.localhost.com:/... with the same error - connection reset by host

I put a breakpoint in my code, and I noticed the code gets called twice (as soon as I exit from trace from last line, it gets called again to the first line).

I have several other actions in this controller, and they all work ok.

Anyone having any idea what am I doing wrong?


EDIT

I started Fiddler, and now my browser shown this error:

[Fiddler] ReadResponse() failed: The server did not return a response for this request. Server returned 0 bytes.


EDIT

I want to mention that webapi is integrated into a legacy classic asp.net application

The initialization code is as follows:

In global.asax.Application_Start

WebApiHelper.Initialize
....
....
Public Class WebApiHelper

  Public Shared Sub Initialize()
    GlobalConfiguration.Configuration.MessageHandlers.Add(New BasicAuthMessageHandler() With { _
             .PrincipalProvider = New MPNAuthProvider() _
           })

    AreaRegistration.RegisterAllAreas()

    WebApiConfig.Register(GlobalConfiguration.Configuration)
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters)
    RouteConfig.RegisterRoutes(RouteTable.Routes)

    GlobalConfiguration.Configuration.EnsureInitialized()
End Sub

....

MPNAuthProvider is used to ensure authenticated access to some webapi controllers

Public Class MPNAuthProvider
  Implements IProviderPrincipal

  Public Function CreatePrincipal(username As String, password As String) As IPrincipal Implements IProviderPrincipal.CreatePrincipal

    Dim userID As Integer = 0
    If Not UserData.ValidateUser(username, password, userID) Then Return Nothing

    Dim identity = New GenericIdentity(userID)
    Dim principal = New GenericPrincipal(identity, {"User"})
    Return principal
  End Function

End Class

Anything else I should check to see what happens?

Thank you

Upvotes: 3

Views: 1354

Answers (1)

bzamfir
bzamfir

Reputation: 4906

Initial Solution

After suggestion from Julien Jacobs, I tested my code into a separate, stand alone webapi project, and indeed the code proved to be correct.

So I started to investigate the web.config.

And I found the following settings that I had to comment out:

<system.web>
....
  <httpModules>
    <add name="RadUploadModule" type="Telerik.Web.UI.RadUploadHttpModule" />
    <add name="RadCompression" type="Telerik.Web.UI.RadCompression" />
  </httpModules>

and

<modules runAllManagedModulesForAllRequests="true">
  <remove name="RadUploadModule" />
  <remove name="RadCompression" />
  <add name="RadUploadModule" type="Telerik.Web.UI.RadUploadHttpModule" preCondition="integratedMode" />
  <add name="RadCompression" type="Telerik.Web.UI.RadCompression" preCondition="integratedMode" />
</modules>

After I commented them, the code started to work ok.

But this proved to not be the ideal solution, so please read on...


Updated solution

After more tests with the application, I realized that RadCompression, while not absolutely required, is very useful to web applications with Telerik Ajax, because it provides transparent, on the fly compression for all ajax traffic (plus viewstate, is configured).

Because I disabled it, the application started to be slower.

So I had to find a way to re-enable RadCompression, but disable it for certain requests (like webapi endpoint for files download).

And the solution is:

Add special config section for RadCompression configuration

<configSections>
 <sectionGroup name="telerik.web.ui">
    <section name="radCompression" type="Telerik.Web.UI.RadCompressionConfigurationSection, Telerik.Web.UI, PublicKeyToken=121fae78165ba3d4" allowDefinition="MachineToApplication" requirePermission="false"/>
  </sectionGroup>
  ....
</configSections>    

Add handlers in system.web\httpModules

<system.web>
  ....
  <httpModules>
    <add name="RadCompression" type="Telerik.Web.UI.RadCompression" />
  </httpModules>

Add handlers in system.webServer\modules

<system.webServer>
  <modules runAllManagedModulesForAllRequests="false">
    <add name="RadCompression" type="Telerik.Web.UI.RadCompression" preCondition="managedHandler" />
  </modules>
</system.webServer>

And the critical part, to disable RadCompression for specific requests (URIs), add a new config section as below

 <telerik.web.ui>
    <radCompression enablePostbackCompression="true">
      <excludeHandlers>
        <!--This will match every api/permitfiles/download file regardless of its location in the web site-->          <add handlerPath="api/permitfiles/download" matchExact="false"/>

      </excludeHandlers>
    </radCompression>
  </telerik.web.ui>

With those changes, RadCompression is empowered globally in the app for all requests, but restricted for specific requests (like webapi files download)

Upvotes: 3

Related Questions