【问题标题】:NodeJS : Validating request type (checking for JSON or HTML)NodeJS:验证请求类型(检查 JSON 或 HTML)
【发布时间】:2013-09-24 23:41:42
【问题描述】:

我想检查我的客户端请求的类型是 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


解决方案(感谢 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"
                    }
                }
            }
        })

【问题讨论】:

    标签: json node.js express


    【解决方案1】:

    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
    }
    

    【讨论】:

    • 感谢您的提示;我正在发出 GET 请求;我可以简单地将标头的完整内容与硬编码字符串进行比较,但这并不干净。我已经在问题的末尾发布了两个请求的调试器摘录。
    • @JoãoRochadaSilva 希望您能解决问题。如果没有,我认为对我的答案的编辑将解决它。
    • 谢谢,那个微妙的*/* Accept 标题是问题所在!非常感谢,我会发布最终有效的代码!
    • 你的正则表达式应该是“/application\/json/”,没有“;”。字符集并不总是在 application/json 内容类型之后发送(例如 POSTMAN)。如果不是,则没有“;”在字符串“application/json”之后。
    【解决方案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】:

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

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

      希望对你有帮助!

      【讨论】:

      • 我必须通过req.accepts(['html', 'json']) === 'json'
      • 这是迄今为止解决这个问题的最佳方案。
      【解决方案4】:

      为了把我的帽子放在戒指上,我想要易于阅读的路线,而不是到处都有.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 上的参考资料。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-18
        • 2023-01-21
        • 1970-01-01
        • 2018-08-25
        相关资源
        最近更新 更多