8

So I have spent weeks researching how to get a pdf from the server via ajax and then show it to the client. The reason I need to do this is a user can't just go to the pdf directly on the server, because they won't provide a token to pass security, but with javascript I can. Using the following code I am able to get what I am expecting:

 var xhr = new XMLHttpRequest();
 xhr.open('GET', url, true);
 xhr.responseType = 'arraybuffer';
 xhr.setRequestHeader("token", token.value);
 xhr.onload = function (e) {
    if (this.status == 200) {
       var file = new Blob([this.response], { type: 'application/pdf' });
       var fileURL = URL.createObjectURL(file);
       window.open(fileURL, '_blank');
    }
 };
 xhr.send();

However when I try and do this exact thing in angular like this (I'm doing half of this stuff just so the headers match in both):

var config = {
    url: url,
    method: 'GET',
    responseType: 'arraybuffer',
    headers: {
        'Accept':'*/*',
        "token": token.value
    }
}

$http(config)
    .success(function (response) {
        var file = new Blob([response], { type: 'application/pdf' });
        var fileURL = URL.createObjectURL(file);
        window.open(fileURL, '_blank')
    });

Here is what the headers look like in both:

GET http://example.com HTTP/1.1
Host: example.com
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36
token: test
Referer: http://example.com
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

The only difference is the xmlhttprequest version has Authorization: Negotiate header, but I can render both of them in fiddler from the responses from the server. This tells me that 'response' from $http isn't the same as this.response in xmlhttprequest. Can someone please tell me what is different and how I can fix it?

I thought for a long time that it was a charset problem and I have even base64 the bytes before sending it over the wire and then used atob() to switch it back, but that didn't seem to make it any better. It seems the $http version takes the response and converts the bytes to utf8 even if they aren't...my bytes are I think are windows-1252/latin1/ios-8895-1

The reason I say this is because I saw this in a hex editor:

btye 85 (on server) switched to two bytes of C2 85 (after $http code) http://www.fileformat.info/info/unicode/char/85/charset_support.htm

btye c5 (on server) switched to two bytes of c3 85 (after $http code) http://www.fileformat.info/info/unicode/char/c5/charset_support.htm

I have even tried to encode/decode if various combinations with no avail. I really think the pdf is getting corrupted some how when it's going through angular, but I don't know how.

UPDATE 1

On the server side the code looks like this:

public class LabelController : Controller
{
  //using Microsoft.AspNet.Mvc
  [HttpGet, Route("api/labels/box")]
  public IActionResult BoxLabels(Guid requestId)
  {
    var data = this.getPDF("http://foobar.com/example.pdf");
    Response.Headers.Add("Content-Length", data.Length.ToString());
    Response.Headers.Add("Content-Disposition", "inline; filename=example.pdf");
    return new FileContentResult(data, "application/pdf");
  }

  //using restsharp
  private bytes[] getPDF(url)
  {
    var client = new RestClient
    {
      BaseUrl = new Uri(url),
      Authenticator = new NtlmAuthenticator()
    };

    var request = new RestRequest
    {
      Method = Method.POST,
      Credentials = CredentialCache.DefaultNetworkCredentials,
      RequestFormat = DataFormat.Xml
    };

    var response = client.Execute(request);
    return response.RawBytes;
  }
}

Here is sort of what it looks like: http://plnkr.co/edit/R3FiesPk7Os22SMsTTfE without the "No 'Access-Control-Allow-Origin'" error

UPDATE 2

Here is what the network traffic tells me: inspection of ajax call

Here is what the pdf looks like when using $http:

blank pdf when using $http

Ian Overton
  • 1,060
  • 7
  • 17
  • can you show the angular html and the directive? Do you have a punker set up that you can share? What are your result exactly? – Sari Rahal May 11 '16 at 17:11
  • If I show the angular html and directive this would get much longer and I'm not sure it adds any value to the question. I'll add what I did on the c# part though – Ian Overton May 11 '16 at 17:23
  • Can you create a plunker to help understand your issue. Here is a link to mine. This shows rendering PDF's in angular. http://plnkr.co/edit/gFfGQCzAfmf2zf5C2Bpc?p=preview – Sari Rahal May 11 '16 at 17:38
  • Not sure this helps as I'm getting a cross-origin error which I do not have in my version because they are on the same domain and I have security on the server. http://plnkr.co/edit/R3FiesPk7Os22SMsTTfE – Ian Overton May 11 '16 at 18:54
  • is it turning your request into an "Option"? – Sari Rahal May 11 '16 at 19:02
  • I don't know what "Option" means. It's turning my pdf into a blob. The url it goes to would look something like this: blob:https%3A//example.com/2af08e2c-d7b5-411e-a33c-28109e6bb04a – Ian Overton May 11 '16 at 19:08
  • Your plunker does works from me and both button clicks open pdf in new window, was anything specific you are looking for? – Shankar Gurav May 17 '16 at 10:21

2 Answers2

1

So I think your issue is that the browser is doing a CORS Preflight because you are doing a cross browser request. It is turning a GET request into an OPTIONS.

enter image description here

You will need to add the OPTIONS request to your accepted CORS Options. You may also need to add your IP address to the accepted Origins within the CORS Options.

Here are some helpful links for this: http://barnabas.tumblr.com/post/87914971548/the-curse-of-the-cors-preflight-and-how-to-defeat Confused about how to handle CORS OPTIONS preflight requests

Community
  • 1
  • 1
Sari Rahal
  • 1,897
  • 2
  • 32
  • 53
  • The plunker example I gave has cross side scriptting problems because I couldn't upload a pdf to plunker, but mine is on the same domain nor find a pdf that has access-control-allow-origin on...it is somehting like example.com/ (ui) and example.com/api (service), however I will double check to make sure. – Ian Overton May 12 '16 at 15:40
  • Nope it is a get method. I added it to my question. – Ian Overton May 12 '16 at 16:04
0

I'm trying to learn Angular, and thought it would be interesting to see if this really is an Angular mangling problem. So I created a simple node webserver and used exactly your code.

I can show a PDF in a browser window using the Angular code you say is failing for you. The only gotcha is that your approach doesn't work in Microsoft's Edge because blob URLs aren't supported for security reasons.

The code is on GitHub and an earlier version is on C9.

I then wondered if this was an IIS/restsharp/ASP.NET MVC problem. However I pretty quickly created an ASP.NET MVC application using restsharp that also works with Angular, using code very similar to yours. This is also on GitHub. Your restsharp code needed a little love, but again it all worked fine first time for me.

So the reason you're not getting any answers here is that I don't think there's enough information in the question to figure out what's wrong in your environment. I hope this stuff is helpful though.

Community
  • 1
  • 1
Rich N
  • 8,939
  • 3
  • 26
  • 33
  • It looks like you've created most of my problem, but you didn't add in the security part of the asp, which i'm starting think might be the problem. – Ian Overton Jun 06 '16 at 15:40