Подтвердить что ты не робот

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() не работает

тоже не работает. Что я делаю не так?


Изменить 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", и я заменил параметр "accepts" полным объектом "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"
                    }
                }
            }
        })
4b9b3361

Ответ 1

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

EDIT:

Хорошо, теперь я понимаю ваш вопрос. Вы говорите о заголовке 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
}

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

Чтобы бросить свою шляпу в кольцо, я хотел легко читаемые маршруты без .json суффиксов .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.