Handling 415 (Unsupported Content-Type) Errors in Hapi
I am working on a project related to Content Security Policy (CSP) reports. As part of this project, I need to record POST requests made by Chrome that are initiated whenever a CSP policy is violated. When Chrome sends these reports, they send the following header:
Content-Type: application/csp-report
The body of the request is very basic JSON.
The application that I wrote to handle these POST requests is written in Node using the Hapi framework. I set up a simple route to handle the request:
server.route({
method: 'POST',
path:'/csp-report',
handler: function (request, reply) {
// Record the request
}
});
All browsers other than Chrome worked just fine; however, Chrome produced a 415
HTTP response when the endpoint was invoked. Other browsers use a content type of application/json
, whereas Chrome uses application/csp-report
. A 415
HTTP status code stands for “Unsupported Media Type”. Simply put, the server cannot handle this unknown media type. I tried a number of ways of getting the server to support the media type, but the best result led to the content being parsed incorrectly, making the previously usable JSON a jumbled mess.
My solution to this issue was to map application/csp-report
to application/json
. In the end, the content really is just JSON, so there is no point in maintaining this content type.
To accomplish this goal, I utilized the onRequest
event to map the content type:
server.ext('onRequest', function(request, reply) {
if ('application/csp-report' === request.headers['content-type']) {
request.headers['content-type'] = 'application/json';
request.headers['x-content-type'] = 'application/csp-report';
}
return reply.continue();
});
This snippet catches the request before it is passed to the routes. If I find the application/csp-report
content type, I simply change it to application/json
. I also set a new x-content-type
header to record the original content type in case I ever need that information in the future.
One small note is that reply.continue()
was well documented in the Hapi docs; however, I found many other tutorials and snippets handling this incorrectly. I suspect that this difference is due to an API change at some point, so your mileage may vary with that call.
There’s nothing spectacular in this code, but I really couldn’t find any useful information about how to handle this issue. Hopefully this helps some wayward soul in the future.