Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS : Validating request type (checking for JSON or HTML)

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 3 documentation at:

http://expressjs.com/api.html

And there are two methods req.accepts() and req.is(), used like this:

req.accepts('json') 

or

req.accepts('html') 

Since these are not working as they should, I tried using:

var requestType = req.get('content-type');

or

var requestType = req.get('Content-Type');

requestType is always undefined...

Using the suggestion at this thread:

Express.js checking request type with .is() doesn't work

does not work either. what am I doing wrong?


Edit 1: I have checked for correct client HTML negotiation. Here are my two different request headers (taken from the debugger inspector):

HTML : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

JSON : Accept: application/json, text/javascript, */*; q=0.01


Solution (thanks to Bret):

Turns out I was specifying the Accept headers wrong, and the */* was the issue. Here is the code that works!

//server (now works)
var acceptsHTML = req.accepts('html');
var acceptsJSON = req.accepts('json');

if(acceptsHTML)  //will be null if the client does not accept html
{}

I am using JSTREE, a jQuery plugin that uses jQuery Ajax calls underneath). The parameters passed to the Ajax call are in the "ajax" field, and I have replaced the "accepts" parameter with a complete "headers" object. Now it works, and should solve the problem when you are using plain jQuery if it should ever occur.

//client 

.jstree({
            // List of active plugins
            "plugins" : [
                "themes","json_data","ui","crrm","cookies","dnd","search","types","hotkeys","contextmenu"
            ],

            "json_data" : {
                "ajax" : {
                    // the URL to fetch the data
                    "url" : function(n) {
                        var url = n.attr ? IDMapper.convertIdToPath(n.attr("id")) : "<%= locals.request.protocol + "://" + locals.request.get('host') + locals.request.url %>";
                        return url;
                    },
                    headers : {
                        Accept : "application/json; charset=utf-8",
                        "Content-Type": "application/json; charset=utf-8"
                    }
                }
            }
        })
like image 512
João Rocha da Silva Avatar asked Sep 19 '13 18:09

João Rocha da Silva


Video Answer


2 Answers

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
}
like image 186
Bret Copeland Avatar answered Oct 02 '22 16:10

Bret Copeland


It's a bit later, but I found this a better solution for me:

req.accepts('html, json') === 'json'

Hope it helps!

like image 32
joserobleda Avatar answered Oct 02 '22 16:10

joserobleda