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"
}
}
}
})
Ответы
Ответ 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.