NodeJS : Validating request type (checking for JSON or HTML)

前端 未结 4 1526
耶瑟儿~
耶瑟儿~ 2020-12-16 13:32

I would like to check if the type that is requested by my client is JSON or HTML, as I want my route to satisfy both human and machine needs.

I have read the Express

相关标签:
4条回答
  • 2020-12-16 13:58

    Please define "not working as they should", because they do for me.

    In other words:

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

    The Content-Type header as sent by a client isn't used for content negotiation, but to declare the content type for any body data (like with a POST request). Which is the reason why req.is() isn't the correct method to call in your case, because that checks Content-Type.

    0 讨论(0)
  • 2020-12-16 14:02

    var requestType = req.get('Content-Type'); definitely works if a content-type was actually specified in the request (I just re-verified this a minute ago). If no content-type is defined, it will be undefined. Keep in mind that typically only POST and PUT requests will specify a content type. Other requests (like GET) will often specify a list of accepted types, but that's obviously not the same thing.

    EDIT:

    Okay, I understand your question better now. You're talking about the Accept: header, not content-type.

    Here's what's happening: notice the */*;q=0.8 at the end of listed accept types? That essentially says "I'll accept anything." Therefore, req.accepts('json') is always going to return "json" because technically it's an accepted content type.

    I think what you want is to see if application/json is explicitly listed, and if so, respond in json, otherwise respond in html. This can be done a couple of ways:

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

    or using a simple regular expression:

    if(/application\/json;/.test(req.get('accept'))) {
        //respond json
    } else {
        //respond in html
    }
    
    0 讨论(0)
  • 2020-12-16 14:07

    To throw my hat in the ring, I wanted easily readable routes without having .json suffixes everywhere or having to have each route hide these details in the handler.

    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"}))
    

    Here's how those middlewares work.

    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") }
    

    Personally I think this is quite readable (and therefore maintainable).

    $ 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"}
    

    Notes:

    • I recommend putting the HTML routes before the JSON routes because some browsers will accept HTML or JSON, so they'll get whichever route is listed first. I'd expect API users to be capable of understanding and setting the Accept header, but I wouldn't expect that of browser users, so browsers get preference.
    • The last paragraph in ExpressJS Guide talks about next('route'). In short, next() skips to the next middleware in the same route while next('route') bails out of this route and tries the next one.
    • Here's the reference on req.accepts.
    0 讨论(0)
  • 2020-12-16 14:14

    It's a bit later, but I found this a better solution for me:

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

    Hope it helps!

    0 讨论(0)
提交回复
热议问题