I would like to check if the type that is requested by my client is JSON or HTML, as I want my route to satisfy both human and machine needs.
I have read the Express
Please define "not working as they should", because they do for me.
In other words:
// server.js
console.log('Accepts JSON?', req.accepts('json') !== undefined);
// client sends 'Accept: application/json ...', result is:
Accepts JSON? true
// client sends 'Accept: something/else', result is:
Accepts JSON? false
The Content-Type
header as sent by a client isn't used for content negotiation, but to declare the content type for any body data (like with a POST
request). Which is the reason why req.is()
isn't the correct method to call in your case, because that checks Content-Type
.
var requestType = req.get('Content-Type');
definitely works if a content-type was actually specified in the request (I just re-verified this a minute ago). If no content-type is defined, it will be undefined. Keep in mind that typically only POST and PUT requests will specify a content type. Other requests (like GET) will often specify a list of accepted types, but that's obviously not the same thing.
EDIT:
Okay, I understand your question better now. You're talking about the Accept: header, not content-type.
Here's what's happening: notice the */*;q=0.8
at the end of listed accept types? That essentially says "I'll accept anything." Therefore, req.accepts('json')
is always going to return "json"
because technically it's an accepted content type.
I think what you want is to see if application/json
is explicitly listed, and if so, respond in json, otherwise respond in html. This can be done a couple of ways:
// a normal loop could also be used in place of array.some()
if(req.accepted.some(function(type) {return type.value === 'application/json';}){
//respond json
} else {
//respond in html
}
or using a simple regular expression:
if(/application\/json;/.test(req.get('accept'))) {
//respond json
} else {
//respond in html
}
To throw my hat in the ring, I wanted easily readable routes without having .json
suffixes everywhere or having to have each route hide these details in the handler.
router.get("/foo", HTML_ACCEPTED, (req, res) => res.send("<html><h1>baz</h1><p>qux</p></html>"))
router.get("/foo", JSON_ACCEPTED, (req, res) => res.json({foo: "bar"}))
Here's how those middlewares work.
function HTML_ACCEPTED (req, res, next) { return req.accepts("html") ? next() : next("route") }
function JSON_ACCEPTED (req, res, next) { return req.accepts("json") ? next() : next("route") }
Personally I think this is quite readable (and therefore maintainable).
$ curl localhost:5000/foo --header "Accept: text/html"
<html><h1>baz</h1><p>qux</p></html>
$ curl localhost:5000/foo --header "Accept: application/json"
{"foo":"bar"}
Notes:
It's a bit later, but I found this a better solution for me:
req.accepts('html, json') === 'json'
Hope it helps!