1
votes

I'm trying to remove the unnecessary pre-flight requests in my application. For it I've simplified some parts of request, removed custom headers etc. But got stuck on a problem - GET requests now work fine without pre-flights, but POST requests still have them.

I've followed the requirements:

  1. Request does not set custom HTTP headers.
  2. Content type is "text/plain; charset=utf-8".
  3. The request method has to be one of GET, HEAD or POST. If POST, content type should be one of application/x-www-form-urlencoded, multipart/form-data, or text/plain.

Both GET and POST requests go through the single httpinvoke call.

As an example - GET request that is not prefaced by pre-flight:

URL: http://mydomain/APIEndpoint/GETRequest?Id=346089&Token=f5h345

Request Method:GET

Request Headers:

Accept:*/*

Accept-Encoding:gzip, deflate

Accept-Language:uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2

Cache-Control:no-cache

Connection:keep-alive

Content-Type:text/plain; charset=utf-8

Host: correct host

Origin:http://localhost

Pragma:no-cache

Referer: correct referer

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

Query String Parameters:

Id=346089

Token=f5h345

And a POST request that looks very similar but is still prefaced with pre-flight:

URL: http://mydomain/APIEndpoint/GETRequest?param=TEST

Request Method:POST

Request Headers:

Accept:*/*

Accept-Encoding:gzip, deflate

Accept-Language:uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2

Cache-Control:no-cache

Connection:keep-alive

Content-Length:11

Content-Type:text/plain; charset=UTF-8

Host:

Origin:http://localhost

Pragma:no-cache

Referer: User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

Query String Parameters:

param:TEST

Request Payload

{MyData: {}}

Any advice would be appreciated! Thanks!

==== Update ===

As requested, posting the pre-flight request for the POST request:

URL: http://mydomain/APIEndpoint/GETRequest?param=TEST

Request Method:OPTIONS

Status Code:200 OK

Response Header

Access-Control-Allow-Origin:*

Cache-Control:no-cache

Content-Length:0

Date:Wed, 09 Aug 2017 08:02:16 GMT

Expires:-1

Pragma:no-cache

Server:Microsoft-IIS/8.5

X-AspNet-Version:4.0.30319

X-Powered-By:ASP.NET

Request Headers

Accept:*/*

Accept-Encoding:gzip, deflate

Accept-Language:uk-UA,uk;q=0.8,ru;q=0.6,en-US;q=0.4,en;q=0.2

Access-Control-Request-Method:POST

Cache-Control:no-cache

Connection:keep-alive

Host:correct host

Origin:http://localhost

Pragma:no-cache

Referer: correct referer

User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36

Query String Parameters

param:TEST

It's a legacy code which uses the httpinvoke library. Code snippet that actually does the call:

_converters: {
    'text json': function (input, reviver) {},
    'json text': JSON.stringify
};

headers = {
    'Content-Type': 'text/plain; charset=utf-8'
};

data = {params: "test"};

httpinvoke(url, method.toUpperCase(), {
    corsExposedHeaders: ['Content-Type'],
    headers: headers,
    input: data,
    converters: _converters,                
    inputType: 'json',
    outputType: 'json',
    timeout: self._getMessageTimeout()
}).then(function (res) {}, function (error) {});
2
To be clear, can you explain exactly how you know the browser isn’t doing a preflight in the case of the GET request? For example, I guess you may have looked into the Network pane in your browser devtools and you see there that the browser is showing a preflight for the POST but not for the GET? And is the case of the POST request is the preflight succeeding or is your browser devtools console showing any kind of error message about the preflight?sideshowbarker
Yes, I use DEV Tools to check the result. And yes, in case of POST pre-flight does succeed. Just to be sure, request urls are correct since they work just fine with the old version of the requests. Old versions had custom fields in headers for both GET and POST version and both generated preflight requests.North
Thanks so to be clear, in the response to the OPTIONS request there’s no Access-Control-Allow-Headers response header? And there’s no Access-Control-Allow-Methods response header, despite the fact that the request has a Access-Control-Request-Method: POST request header?sideshowbarker
Yes, you are correct. I've just rechecked the options request. There's no Access-Control-Allow-Headers and Access-Control-Allow-Methods. Though if I revert all the changes to the previous working solution (difference being Content-Type set to application/json and 2 custom fields in the header) - methods that you've mentioned are present.North
OK well the only other suggestion I can offer at this point as far as trying to get some additional help with identifying what’s happening would be if you could paste in a snippet of code that shows how you’re making the request. At this point the question doesn’t mention anything at all about how you’re doing that (e.g., whether you’re using XHR or the Fetch API or an Ajax method from a JavaScript library, or what)—or whether you’ve tried the request with some other method (e.g., tried it with the Fetch API instead of XHR) but observed the same problem.sideshowbarker

2 Answers

4
votes

This could happen if there are event listeners registered on the XMLHttpRequestUpload object (that forces a preflight; see the note on the use-CORS-preflight flag in https://xhr.spec.whatwg.org/, and a related note in https://fetch.spec.whatwg.org/ and the updated documentation on CORS preflight requests in the MDN CORS article).

Does httpinvoke do that?

2
votes

As @Anne mentioned the reason that POST were sending pre-flight requests despite the requests themselves conforming to the rules of "simple requests" (and thus not needing a pre-flight) was in the XMLHttpRequestUpload event listeners.

XMLHttpRequestUpload itself might not be mentioned in the code, but you can always find it in xhr.upload variable. This was the case for http-invoke library.

So totally innocent looking code like:

xhr.upload.onprogress = onuploadprogress;

actually causes mandatory pre-flight requests.

Thanks to all who helped solve this problem.