it-roy-ru.com

NodeJS: проверка типа запроса (проверка на наличие JSON или HTML)

Я хотел бы проверить, является ли тип, который запрашивает мой клиент, JSON или HTML, так как я хочу, чтобы мой маршрут удовлетворял как человеческим, так и машинным потребностям.

Я прочитал документацию по Express 3 по адресу:

http://expressjs.com/api.html

И есть два метода req.accepts() и req.is(), используемые следующим образом:

req.accepts('json') 

или же 

req.accepts('html') 

Поскольку они не работают должным образом, я попытался использовать:

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

или же

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

requestType всегда undefined...

Используя предложение в этой теме:

Express.js не проверяет тип запроса с помощью .is ()

тоже не работает. Что я делаю неправильно? 


Edit 1: я проверил правильность согласования клиентского HTML. Вот мои два разных заголовка запроса (взяты из инспектора отладчика): 

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

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


Решение (спасибо Брету):

Оказывается, я неправильно указал заголовки Accept, и проблема была в */*. Вот код, который работает!

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

Я использую JSTREE, плагин jQuery, который использует вызовы jQuery Ajax внизу). Параметры, передаваемые в вызов Ajax, находятся в поле «ajax», и я заменил параметр «accept» на полный объект «headers». Теперь это работает, и должно решить проблему, когда вы используете простой jQuery, если это когда-либо произойдет.

//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
João Rocha da Silva

var requestType = req.get('Content-Type'); определенно работает, если в запросе действительно указан тип содержимого (я только что проверил это минуту назад). Если тип содержимого не определен, он будет неопределенным. Имейте в виду, что обычно только POST и PUT-запросы будут указывать тип контента. Другие запросы (например, GET) часто указывают список принятых типов, но это, очевидно, не одно и то же.

Правка:

Хорошо, теперь я понимаю ваш вопрос лучше. Вы говорите о заголовке Accept:, а не о типе контента.

Вот что происходит: обратите внимание на */*;q=0.8 в конце перечисленных типов принятия? По сути, это говорит: «Я приму все». Поэтому req.accepts('json') всегда будет возвращать "json", потому что технически это принятый тип контента.

Я думаю, что вы хотите, чтобы увидеть, если application/json явно указан, и если так, ответить в json, в противном случае ответить в html. Это можно сделать несколькими способами:

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

или используя простое регулярное выражение:

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

Это немного позже, но я нашел это лучшее решение для меня:

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

Надеюсь, поможет!

7
joserobleda

Пожалуйста, определите " не работает, как они должны ", потому что они делают для меня.

Другими словами:

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

Заголовок Content-Type, отправленный клиентом, используется не для согласования содержимого, а для объявления типа содержимого для любых основных данных (например, с запросом POST). По этой причине req.is() не является правильным методом для вызова в вашем случае, потому что он проверяет Content-Type.

2
robertklep

Чтобы бросить свою шляпу в кольцо, я хотел легко читаемые маршруты без повсеместных суффиксов .json или необходимости, чтобы каждый маршрут скрывал эти детали в обработчике.

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

Вот как работают эти промежуточные программы.

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

Лично я думаю, что это вполне читабельно (и, следовательно, ремонтопригодно).

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

Заметки:

  • Я рекомендую размещать маршруты HTML перед маршрутами JSON, потому что некоторые браузеры будут принимать HTML или JSON, поэтому они получат тот маршрут, который указан первым. Я ожидаю, что пользователи API будут способны понимать и устанавливать заголовок Accept, но я не ожидаю, что это будут пользователи браузеров, поэтому браузеры получают предпочтение.
  • последний абзац в руководстве ExpressJS рассказывает о следующем («маршрут»). Короче говоря, next () переходит к следующему промежуточному программному обеспечению по тому же маршруту, в то время как next ('route') выходит из этого маршрута и пробует следующий.
  • Вот ссылка на req.accepts .
0
mLuby