NodeJS:验证请求类型(检查JSON或HTML)

时间:2013-09-19 18:26:54

标签: json node.js express

我想检查我的客户端请求的类型是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 checking request type with .is() doesn't work

也不起作用。我究竟做错了什么?


编辑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 Ajax调用的jQuery插件。传递给Ajax调用的参数位于“ajax”字段中,我已将“accepted”参数替换为完整的“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 个答案:

答案 0 :(得分:13)

如果在请求中实际指定了内容类型,那么

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
}

答案 1 :(得分:9)

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

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

希望它有所帮助!

答案 2 :(得分:2)

请定义“不能正常工作”,因为它们适合我。

换句话说:

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

答案 3 :(得分: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路由之前,因为某些浏览器会接受HTML JSON,因此它们将获得最先列出的路由。我希望API用户能够理解和设置Accept标头,但是我不希望浏览器用户使用,因此浏览器会获得优先选择。
  • ExpressJS指南中的last paragraph讨论了next('route')。简而言之,next()跳到同一路由中的下一个中间件,而next('route')退出该路由并尝试下一个中间件。
  • 这里是req.accepts上的参考。