Reputation: 5263
I am working on re-writing an existing web site using Node.js with Express.
Front-end of the site will use Backbone JS and thus I need to have all necessary routes comply with native Backbone sync. Now most of URL's the client and for Backbone sync will be same. But they won't work for regular GET as they would need to return JSON.
So I am thinking, would it be a good idea to add extension to Model/Collection URLs in Backbone, such as .json
, and in Express to have this for every route:
app.get('/p/:topCategory/:category/:product.:format', function(req, res) { ... });
Where if (req.params.id == 'json')
than we send JSON, otherwise we render HTML?
Or is there a better approach? Please help.
Upvotes: 7
Views: 6777
Reputation: 596
I think the correct way to do this is to implement content negotiation into your application. Yeah, Express 3.x facility is a way to do it and provides a direct answer to your question, but I don't think it's the best way to do it because it places the content negotiation responsibility in the routing logic. I don't think that's a good place for it because it doesn't follow the single responsibility principle, having put the content negotiation in routing logic.
I've taken a stab at implementing content negotiation in my blog engine; reviewing that might help guide you in a good direction. The gist is that the code determines the file extension via content negotiation logic. Then, with the file extension, it wants to find the corresponding view file, renders it into a response and sends it back to the client. The idea is that it responds with the requested resource in the requested representation per the content negotiation. The routing logic only specifies a view, but has no idea about content negotiation. That happens outside of the routing logic which makes for a more flexible design.
The result of this design is the ability to ask for a specific representation of the resource like:
http://blog.joeyguerra.com/index.json and get a JSON representation http://blog.joeyguerra.com/index.phtml and get partial (or an HTML fragment) HTML representation http://blog.joeyguerra.com/index.xml and get an XML representation.
Upvotes: 2
Reputation: 1438
An alternative that also checks that the "X-Requested-With" header is set for jQuery et al.
var onlyAllowJsonRequests = function (req, res, next) {
var acceptJson = (req.accepted.length && _.any(req.accepted, function (acc) { return acc.value.indexOf("json") !== -1 }));
// also check that "X-Requested-With": "XMLHttpRequest" header is set
if (acceptJson && (req.xhr === true)) {
next();
} else {
res.send(406, "Not Acceptable");
}
};
app.use(onlyAllowJsonRequests);
NB underscore is a depency.
Upvotes: 2
Reputation: 63663
The better way of doing this would be to use the content negotiation feature in Express 3.x, namely res.format
:
https://github.com/visionmedia/express/blob/master/lib/response.js#L299-378
res.format({
text: function(){
res.send('hey');
},
html: function(){
res.send('<p>hey</p>');
},
json: function(){
res.send({ message: 'hey' });
}
});
You approach is also ok, Yammer for ex. is using the same approach: http://developer.yammer.com/api/#message-viewing
Upvotes: 12
Reputation: 15003
Use Accept
headers in your requests: Accept: application/json
if you want to receive JSON, Accept: text/HTML
if you want HTML.
Upvotes: 6