Node.js 基于内容类型报头的Expressjs路由器

Node.js 基于内容类型报头的Expressjs路由器,node.js,api,express,middleware,Node.js,Api,Express,Middleware,对于路由,我希望我的中间件将/html文件夹中定义的路由请求传递给服务器html(ejs),如果头内容类型是application/json,则使用/api文件夹中定义的路由 但我不想在每一条路线上都定义它。 所以我不是在寻找定义某些req.api属性的中间件,我可以在每个路由中检查这些属性 app.get('/', function(req, res) { if(req.api_call) { // serve api } else { // s

对于路由,我希望我的中间件将/html文件夹中定义的路由请求传递给服务器html(ejs),如果头内容类型是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);
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");
});
这可能吗?如果可能,我该如何做

我希望它能像这样工作:

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

您可以作为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"});
});

旧答案

我能够像这样成功地将请求回调放在express前面,并看到它成功地修改了传入URL,然后像这样影响express路由:

// 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);
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");
});
这个示例(我测试过)只是在URL的前面添加了“/api”,但是您可以自己检查传入的请求类型,然后根据需要修改URL。我还没有探讨是否可以完全在Express中实现这一点


在这个例子中,当我请求“/”时,我得到了JSON。

为了让我的帽子戴在戒指上,我想要易于阅读的路由,而不需要到处都有
.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"}))
我个人认为这是相当可读的(因此是可维护的)

$curl localhost:5000/foo——标题“Accept:text/html”
bazqux

$curl localhost:5000/foo——标题“Accept:application/json” {“foo”:“bar”}
注:

  • 我建议将
    HTML
    路由放在
    JSON
    路由之前,因为有些浏览器会接受HTML或JSON,所以它们会得到最先列出的路由。我希望API用户能够理解和设置Accept头,但我不希望浏览器用户能够理解和设置Accept头,所以浏览器会得到优先权
  • 文章的最后一段谈到了
    next('route')
    。简而言之,
    next()
    跳过同一路由中的下一个中间件,而
    next('route')
    退出此路由并尝试下一个路由
  • 这是我的参考资料

请参见此处关于检查请求内容类型的内容-我知道如何检查请求内容类型。我面临的问题是希望根据内容类型使用不同的express路由器,而不仅仅是像
app.use('/api',apiRouter)这样的url
Doessence路由器本质上是按路径工作的,我想知道您是否可以使用一些中间件来检查内容类型,并将路径修改为伪路径,其中包含表示内容类型的内容,然后将路由器设置为服务于修改后的路径。例如,对JSON内容类型为
'/'
的请求可以修改为
'/api/'
的伪路径,因此您可以使用
app.get('/api/',…)
提供该请求。我不完全确定在您修改请求中的路径后路由器是否仍然工作,但您可以很容易地尝试。如果不在路由器函数中添加那些
if
语句,这可能是不可能的,例如
app.use()
似乎没有实现任何附加的限制,但是path。@jfriend00不是path只读的吗?在答案中添加了多个版本。最新的研究表明,中间件可以将URL修改为伪路径,然后由Express路由器进行匹配。因此,您可以在其自己的路由器中设置每一组请求,并使用一个简单的中间件功能,通过简单地将
req.url
修改为伪url,将请求定向到相应的路由器。IMHO,我认为这是一个“肮脏”的黑客攻击,但它是有效的,因此w/e xD感谢老兄!我真的做到了!太棒了@CreasolDev-这有点像黑客,但它是一个相当干净的黑客,因为它唯一依赖的是您可以在早期中间件中更改
req.url
,该中间件也用于其他原因。除此之外,它是直接的路由,因此它实际上是一个相当干净的黑客行为。在
路由器中,
HTML\u接受的
和JSON\u接受的`函数看起来干净,感觉干净。获取
定义。不过,到目前为止,我已经决定采用“单点退出”式路由,所有不同的路由只需处理所需的数据,并将其全部传递到最后一个
响应器
,该响应器根据
内容类型提供数据。它为HTML呈现EJS页面或为
内容类型:Application/JSON
提供JSON响应,但感谢您的响应,看起来很棒!