Making XDomainRequests play nice with Node/express/body-parser
I recently spent a couple of days working with XDomainRequests. This is the legacy technology that allow you to do cross origin requests (CORS) on Internet Explorer 8 and 9. To explain CORS further: if you have a web app available through “myfirstdomain.com” and that web app, once it is loaded in the browser, sends a request to an API that is available on “myseconddomain.com”, that is CORS. Requests between different domains.
Now, this blogs title is “makeitnew.io”, and I will be the first to admit that this posts relevance might be missing. But as it took me a really long time to find a solution for this in my network, and on Google, I feel that I need to write this post to help the next person, implementing legacy browser support for his REST API, to get this problem solved more quickly.
My issue manifested itself after deploying a piece of client side JavaScript that supported XDomainRequests to all our clients. Suddenly alerts about a huge spike in 400 errors filled our system health channel on slack. The number of errors, and the lack of decrease in HTTP response 200, makes it relatively clear that most of the extra traffic we were expecting to get from rolling out support for IE8 and IE9 are all coming in as errors.
I immediately rolled back the change. After the back end looks healthy again, I immediately started looking for reasons why this would happen. Former Microsoft employee Eric Law has written probably the most comprehensible guide to XDomainRequests, and I read through it with a looking glass to find possible explanations.
The first likely culprit is that XDomainRequests are supposed to send requests with the content-type “text/plain” in the header. Yes, I missed the note in Eric Law’s post saying that as of 2014 no content-type seems to be sent. So anyways, I open Postman and start sending requests with the culprit content-type and get 400s back. Time for a pull request on our collector service.
So after setting up everything I need to test locally, I add support for body-parser to parse text/plain on our Node.js / Express back end collection service. Still just 400 back. WAT.
So I am using Internet Explorer 11 in IE9 emulation mode for sending the requests. And the request bodies being sent appear empty. I enable some logging in the console. And, no, the request body is not at all empty. Weirdness is going on here. I get some logging going on my local fork of the collection service. And yes, the requests are in fact empty except for the header.
At this point I am confused and I start reaching out to both my network and Google to figure out if others have experienced the same problem. And there is not much help to be had. I find some colleagues that have experienced something similar and have solved it by creating a proxy for requests with the same domain as their service. Allowing them to use XmlHttpRequests in old Internet Explorer instead. As the script I am making is running across a lot of different domains that I do not control, this is not an alternative. So I keep on Googling.
My breakthrough comes when a colleague suggests that I use PyProxy. After firing up Wireshark (because that was what I had installed) and sending some packages, I discover two things. First, that there are infact a request body that looks healthy. And secondly that the content-type is missing.
After realising the issue was actually the content-type missing, I was able to find a package called “express-content-type-override”. And while it was not perfect, it was enough to fix the problem for my case.
What the aforementioned package lacks to account for, is that the charset also goes in the content-type. That caused some initial problems that was quick to find and fix.
I also submitted a pull request for this: https://github.com/rbartoli/express-content-type-override/pull/1
What is important is that the content type is checked and modified before bodyParser is added to as middleware.
This code is all in the server.js file. Which is where the routing happens in the mentioned application.
This is put in the top of the file, together with the rest of the required packages.
var contentTypeOverride = require('express-content-type-override');
The middleware is applied right before the routing happens:
// Handle XDomainRequests coming in without content-type header. app.use('/api/send', contentTypeOverride({ contentType: 'application/json', charset: 'utf-8' })); var apiSendRouter = require('./routes/api-send'); app.use('/api/send', apiSendRouter);
Parsing of the body happens inside the required file for the path (./routes/api-send)
Magnus Skaalsveen is a consultant in Netlight Consulting. He enjoys working with front end development and user experience.