web-dev-qa-db-fra.com

NodeJS: Validation du type de requête (vérification de JSON ou HTML)

Je voudrais vérifier si le type demandé par mon client est JSON ou HTML, car je souhaite que mon itinéraire réponde aux besoins des utilisateurs et des machines.

J'ai lu la documentation d'Express 3 à l'adresse:

http://expressjs.com/api.html

Et il y a deux méthodes req.accepts() et req.is(), utilisées comme ceci:

req.accepts('json') 

ou 

req.accepts('html') 

Puisque ceux-ci ne fonctionnent pas comme ils le devraient, j'ai essayé d'utiliser:

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

ou

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

requestType est toujours undefined...

En utilisant la suggestion à ce fil:

Le type de requête de vérification Express.js avec .is () ne fonctionne pas

ne fonctionne pas non plus. Qu'est-ce que je fais mal? 


Edit 1 : J'ai vérifié si la négociation HTML du client était correcte. Voici mes deux en-têtes de requête différents (issus de l'inspecteur du débogueur): 

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 (merci à Bret):

Il se trouve que je spécifiais mal les en-têtes Accepter et que le */* était le problème. Voici le code qui fonctionne!

//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
{}

J'utilise JSTREE, un plugin jQuery qui utilise les appels jQuery Ajax ci-dessous). Les paramètres transmis à l'appel Ajax se trouvent dans le champ "ajax" et j'ai remplacé le paramètre "accept" par un objet "en-têtes" complet. Maintenant, cela fonctionne et devrait résoudre le problème lorsque vous utilisez plain jQuery, si cela devait se produire.

//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"
                    }
                }
            }
        })
14

var requestType = req.get('Content-Type'); fonctionne définitivement si un type de contenu a été spécifié dans la requête (je viens de revérifier cela il y a une minute). Si aucun type de contenu n'est défini, il sera indéfini. Gardez à l'esprit que, généralement, seules les demandes POST et PUT spécifieront un type de contenu. D'autres requêtes (comme GET) spécifieront souvent une liste de types acceptés, mais ce n'est évidemment pas la même chose.

MODIFIER:

D'accord, je comprends mieux votre question maintenant. Vous parlez de l'en-tête Accept:, pas du type de contenu.

Voici ce qui se passe: remarquez le */*;q=0.8 à la fin des types d'acceptation répertoriés? Cela dit essentiellement "j'accepterai n'importe quoi". Par conséquent, req.accepts('json') retournera toujours "json" car, techniquement, il s'agit d'un type de contenu accepté.

Je pense que ce que vous voulez, c'est voir si application/json est explicitement listé, et si oui, répondez en json, sinon répondez en html. Cela peut être fait de plusieurs manières:

// 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
}

ou en utilisant une expression régulière simple:

if(/application\/json;/.test(req.get('accept'))) {
    //respond json
} else {
    //respond in html
}
13
Bret Copeland

C'est un peu plus tard, mais j'ai trouvé cela une meilleure solution pour moi:

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

J'espère que ça aide!

7
joserobleda

Veuillez définir " ne fonctionne pas comme ils le devraient ", car ils le font pour moi.

En d'autres termes:

// 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

L'en-tête Content-Type envoyé par un client n'est pas utilisé pour la négociation de contenu, mais pour déclarer le type de contenu de toutes les données de corps (comme dans le cas d'une demande POST). Quelle est la raison pour laquelle req.is() n'est pas la bonne méthode à appeler dans votre cas, car cela vérifie Content-Type.

2
robertklep

Pour lancer mon chapeau dans le ring, je voulais des itinéraires faciles à lire sans avoir partout les suffixes .json ni avoir à chaque itinéraire masquer ces détails dans le gestionnaire.

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"}))

Voici comment ces middlewares fonctionnent.

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") }

Personnellement, je pense que ceci est assez lisible (et donc maintenable).

$ 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"}

Remarques:

  • Je recommande de placer les routes HTML avant les routes JSON, car certains navigateurs accepteront HTML ou JSON, afin d’obtenir la route indiquée en premier. Je m'attendrais à ce que les utilisateurs d'API soient capables de comprendre et de définir l'en-tête Accepter, mais je ne m'attendrais pas à ce que les utilisateurs de navigateurs, de sorte que les navigateurs aient la préférence.
  • Le dernier paragraphe dans ExpressJS Guide parle de next ('route'). En bref, next () passe au middleware suivant sur le même itinéraire, tandis que next ('route') quitte cette route et tente la suivante.
  • Voici la référence sur req.accepts .
0
mLuby