19

我想检查我的客户端请求的类型是 JSON 还是 HTML,因为我希望我的路由能够同时满足人类和机器的需求。

我已阅读 Express 3 文档:

http://expressjs.com/api.html

并且有两种方法req.accepts()and 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


解决方案(感谢 Bret):

原来我指定的 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 Ajax 调用的 jQuery 插件)。传递给 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"
                    }
                }
            }
        })
4

4 回答 4

19

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
}
于 2013-09-19T18:44:10.240 回答
12

有点晚了,但我发现这对我来说是一个更好的解决方案:

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

希望能帮助到你!

于 2014-11-26T19:04:48.870 回答
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.

于 2013-09-19T18:47:56.157 回答
0

为了把我的帽子放在戒指中,我想要易于阅读的路线,而不是.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 路由之前,因为某些浏览器会接受 HTMLJSON,因此它们会获取首先列出的任何路由。我希望 API 用户能够理解和设置 Accept 标头,但我不希望浏览器用户如此,因此浏览器会获得偏好。
  • ExpressJS 指南的最后一段谈到了 next('route')。简而言之,next() 跳到同一路由中的下一个中间件,而 next('route') 退出该路由并尝试下一个。
  • 这是req.accepts上的参考。
于 2018-12-13T22:42:27.270 回答