最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - Express catchall passthrough route based on req.header.accepts - Stack Overflow

programmeradmin2浏览0评论

I'm trying to figure out how to do something a bit unusual: basically I have a server that's both a combination Web frontend with client-side routing and a JSON-based API with server-side routing, but here's the tricky part: they use the same URLs, but what's returned is based on the HTTP Accepts header. So if you were to visit, say, /users/bob in your browser you'd see Bob's profile rendered in HTML, but if you called /users/bob with the Accepts header set to application/json you'd see Bob's profile as a JSON object.

The Web frontend is all client-side routing, so basically what I want to do is this: whatever the actual request path is, if the accepts header is not application/json, it should simply return the static HTML homepage (where all routing is handled by React Router); if the accepts header is application/json, it should pass it on to the rest of the route logic.

I've tried it this way:

import fs from "fs/promises";

const router = express.Router();
const staticPage = await fs.readFile("./frontend/dist/index.html", "utf-8"); // This just returns the index.html file

router.use(async (req, res, next) => { 

    if (req.headers.accept != "application/json") {
        res.status(200).send(staticPage);
    } else {
        
        router.get("/", async (req, res) => {
          res.status(200).json({ hello: "world" });
        });

        router.get("/users/:id", async (req, res) => {
            res.status(200).json({ id: req.params.id });
          });

        //Etc etc

    }

    next();

})

but this gives me Error: Can't set headers after they are sent.

What am I missing here? How do I just tell the router to serve the static HTML page if the accepts header is anything but application/json but do the complex routing logic if it is? Do I need to put next() within my conditional block? None of the other related answers seem to be quite the same situation.

(And yes, I realize this is a weird way of handling app structure and that I need to deal with loading images or CSS or any other files as well, but that's my next step. First I need to sort this out.)

I'm trying to figure out how to do something a bit unusual: basically I have a server that's both a combination Web frontend with client-side routing and a JSON-based API with server-side routing, but here's the tricky part: they use the same URLs, but what's returned is based on the HTTP Accepts header. So if you were to visit, say, /users/bob in your browser you'd see Bob's profile rendered in HTML, but if you called /users/bob with the Accepts header set to application/json you'd see Bob's profile as a JSON object.

The Web frontend is all client-side routing, so basically what I want to do is this: whatever the actual request path is, if the accepts header is not application/json, it should simply return the static HTML homepage (where all routing is handled by React Router); if the accepts header is application/json, it should pass it on to the rest of the route logic.

I've tried it this way:

import fs from "fs/promises";

const router = express.Router();
const staticPage = await fs.readFile("./frontend/dist/index.html", "utf-8"); // This just returns the index.html file

router.use(async (req, res, next) => { 

    if (req.headers.accept != "application/json") {
        res.status(200).send(staticPage);
    } else {
        
        router.get("/", async (req, res) => {
          res.status(200).json({ hello: "world" });
        });

        router.get("/users/:id", async (req, res) => {
            res.status(200).json({ id: req.params.id });
          });

        //Etc etc

    }

    next();

})

but this gives me Error: Can't set headers after they are sent.

What am I missing here? How do I just tell the router to serve the static HTML page if the accepts header is anything but application/json but do the complex routing logic if it is? Do I need to put next() within my conditional block? None of the other related answers seem to be quite the same situation.

(And yes, I realize this is a weird way of handling app structure and that I need to deal with loading images or CSS or any other files as well, but that's my next step. First I need to sort this out.)

Share Improve this question asked Feb 7 at 12:53 Joshua EllisJoshua Ellis 11 silver badge New contributor Joshua Ellis is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

1 Answer 1

Reset to default 2

You have a couple of problems here.

In the not json case, you are calling next() after you have already called send(). Calling next() lets the next handler in the chain run, if it calls send() you will get the "...set headers..." error.

Also, every time your use() handler is called, you add the two get() handlers unnecessarily to the chain of handlers again.

Your code should look something like this:

router.use(async (req, res, next) => { 
  if (req.headers.accept != "application/json") {
    res.status(200).send(staticPage);
  } else {
    next();
  }
})

router.get("/", async (req, res) => {
  res.status(200).json({ hello: "world" });
});

router.get("/users/:id", async (req, res) => {
  res.status(200).json({ id: req.params.id });
});
发布评论

评论列表(0)

  1. 暂无评论