Expressjs路由器基于Content-Type标头

时间:2016-03-09 16:13:17

标签: node.js api express middleware

对于路由,我希望我的中间件将请求在/ html文件夹中定义的路由传递给服务器HTML(ejs),如果标头Content-Type是application / json,则使用在/ api文件夹。

但我不想在每条路线中定义。 所以我不是在寻找定义一些req.api属性的中间件,我可以在每条路径中检查这些属性

app.get('/', function(req, res) {
    if(req.api_call) {
        // serve api
    } else {
        // serve html
    }
});

但我喜欢这样的事情:

// HTML folder
app.get('/', function(req, res) {
    res.send('hi');
});

// API folder
app.get('/', function(req, res) {
    res.json({message: 'hi'});
});

这可能吗?如果可以,我该怎么做?

我喜欢这样的工作:

app.use(checkApiCall, apiRouter);
app.use(checkHTMLCall, htmlRouter);

2 个答案:

答案 0 :(得分:8)

您可以在Express链中插入第一个中间件,一个检查请求类型的中间件处理程序,然后通过向其添加前缀路径将req.url修改为伪URL。然后,此修改将强制该请求仅转到特定路由器(设置为处理该特定URL前缀的路由器)。我已使用以下代码在Express中验证了此功能:

var express = require('express');
var app = express();
app.listen(80);

var routerAPI = express.Router();
var routerHTML = express.Router();

app.use(function(req, res, next) {
    // check for some condition related to incoming request type and
    // decide how to modify the URL into a pseudo-URL that your routers
    // will handle
    if (checkAPICall(req)) {
        req.url = "/api" + req.url;
    } else if (checkHTMLCall(req)) {
        req.url = "/html" + req.url;
    }
    next();
});

app.use("/api", routerAPI);
app.use("/html", routerHTML);

// this router gets hit if checkAPICall() added `/api` to the front
// of the path
routerAPI.get("/", function(req, res) {
    res.json({status: "ok"});
});

// this router gets hit if checkHTMLCall() added `/api` to the front
// of the path
routerHTML.get("/", function(req, res) {
    res.end("status ok");
});

注意:我没有填写checkAPICall()checkHTMLCall()的代码,因为您并不完全具体说明您希望这些代码如何运作。我在自己的测试服务器中嘲笑它们,看看这个概念是否有效。我假设您可以为这些函数提供适当的代码,或者替换您自己的if语句。

先前的答案

我刚刚确认您可以在Express中间件中更改req.url,因此如果您有一些修改req.url的中间件,则会影响该请求的路由。

// middleware that modifies req.url into a pseudo-URL based on 
// the incoming request type so express routing for the pseudo-URLs
// can be used to distinguish requests made to the same path 
// but with a different request type
app.use(function(req, res, next) {
    // check for some condition related to incoming request type and
    // decide how to modify the URL into a pseudo-URL that your routers
    // will handle
    if (checkAPICall(req)) {
        req.url = "/api" + req.url;
    } else if (checkHTMLCall(req)) {
        req.url = "/html" + req.url;
    }
    next();
});

// this will get requests sent to "/" with our request type that checkAPICall() looks for
app.get("/api/", function(req, res) {
    res.json({status: "ok"});
});

// this will get requests sent to "/" with our request type that checkHTMLCall() looks for
app.get("/html/", function(req, res) {
    res.json({status: "ok"});
});

旧答案

我能够在这样的快递面前成功地发出请求回调,并看到它成功修改了传入的URL,然后像这样影响快速路由:

var express = require('express');
var app = express();
var http = require('http');

var server = http.createServer(function(req, res) {
    // test modifying the URL before Express sees it
    // this could be extended to examine the request type and modify the URL accordingly
    req.url = "/api" + req.url;
    return app.apply(this, arguments);
});

server.listen(80);

app.get("/api/", function(req, res) {
    res.json({status: "ok"});
});

app.get("/html/", function(req, res) {
    res.end("status ok");
});

这个例子(我测试过的)只是硬连线添加" / api"在URL的前面,但您可以自己检查传入的请求类型,然后根据需要进行URL修改。我还没有探究这是否可以完全在Express中完成。

在这个例子中,当我要求" /"时,我获得了JSON。

答案 1 :(得分:1)

要戴上帽子,我希望路线易于阅读,而不必到处都有.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 Guide中的最后一段讨论了next('route')。简而言之,next()跳到同一路由中的下一个中间件 ,而next('route')退出此路由并尝试下一条。
  • 这里是req.accepts上的参考。