Search code examples
asp.netajaxangularasp.net-web-apixmlhttprequest

Angular / Asp.Net Web Api request binary data


I have an Angular 4 application which consumes an Asp.Net Web Api, and I want the Api to return a binary file. The Api route seems to be working correctly - I tested it using a rest console and the response is as expected. However, when trying to use the same route in the Angular app, the request sends but returns an error. I can see with the C# debugger that the request is executing completely and doesn't fail on the server. Here's the error in the JS console:enter image description here

This error occurs on all browsers tested (Chrome, Firefox, IE, Edge, Safari).

Here's the server side code:

[Route("api/getfile")]
public IHttpActionResult GetFile()
{
    byte[] file = <file generator code>;
    System.Net.Http.HttpResponseMessage responseMessage = new System.Net.Http.HttpResponseMessage
    {
        Content = new System.Net.Http.StreamContent(new System.IO.MemoryStream(file))
    };
    responseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
    responseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    responseMessage.Content.Headers.ContentDisposition.FileName = "file.pdf";
    return this.ResponseMessage(responseMessage);
}

And here's the Angular code:

let headers = new Headers({
  "Accept": "application/octet-stream",
  "X-Requested-With": "XMLHttpRequest",
  "Authorization": `Bearer ${token}`
});
let opts = new RequestOptions({headers = headers});
opts.responseType = ResponseContentType.Blob;
// Uses old @angular/http, not HttpClient
this.http
  .get(`${apiUrl}/getfile`, opts)
  .map(res => res.blob())
  .catch(err => handleError(err));

EDIT: I tried using a plain XMLHttpRequest instead of Angular's Http service and it works. What do I need to do to get this to work with Angular?

EDIT 2: It works if I fetch an actual file on the file system that's accessible using the same host that the Angular app is running on. The Angular app is on localhost:8080, while the api is on a different port. If I expose a file on localhost:8080 (e.g., in the build folder) than I can fetch that file. This makes me wonder if it's a security issue, or maybe has to do with the headers or the way Web Api returns the binary data.


Solution

  • The second answer by Crying Freeman, using Response directly, does work, but it bypasses Owin's processing and would mean having to manually implement things like CORS or anything else normally handled using CORS.

    I found another solution, to use a custom formatter to allow returning a byte array from the controller method. This is also nicer because I don't need to set any headers manually, not even Content-Type.