Express.js 중첩 라우터로 휴식


136

대략 다음과 같은 REST 엔드 포인트를 원한다고 가정하십시오.

/user/
/user/user_id 

/user/user_id/items/
/user/user_id/items/item_id

각각의 CRUD는 의미가 있습니다. 예를 들어, / user POST는 새 사용자를 작성하고 GET은 모든 사용자를 가져옵니다. / user / user_id GET은 한 명의 사용자 만 가져옵니다.

항목은 사용자마다 다르므로 user_id ( 특정 사용자) 아래에 놓습니다 .

이제 익스프레스 라우팅을 모듈화하기 위해 몇 가지 라우터 인스턴스를 만들었습니다. 사용자를위한 라우터와 아이템을위한 라우터가 있습니다.

var userRouter = require('express').Router();
userRouter.route('/')
  .get(function() {})
  .post(function() {})
userRouter.route('/:user_id')
  .get(function() {})

var itemRouter = require('express').Router();
itemRouter.route('/')
  .get(function() {})
  .post(function() {})
itemRouter.route('/:item_id')
  .get(function() {})

app.use('/users', userRouter);

// Now how to add the next router?
// app.use('/users/', itemRouter);

의 URL item은의 URL 계층 구조의 하위 항목입니다 user. 이제 /usersuserRouter에 대한 것이지만 itemRouter에 대한보다 구체적인 경로는 /user/*user_id*/items/무엇입니까? 또한 가능한 경우 item_uter에서도 user_id에 액세스 할 수 있기를 바랍니다.


이 문제를 해결하기 위해 Express를 사용하는 것에 대한 훌륭한 답변이 이미 있습니다. 그러나 Express에 빌드 된 Loopback을 사용하여 Swagger 기반 API를 구현하고 요청한대로 CRUD를 수행하기 위해 모델간에 관계를 추가 할 수 있습니다. 좋은 점은 초기 학습 곡선 후에 조립하는 것이 훨씬 빠릅니다. loopback.io
— Mike S.

답변:


278

라우터를 사용하거나 사용하지 않고 다른 라우터에 미들웨어 로 연결하여 라우터를 중첩 할 수 있습니다 params.

상위 라우터에서 {mergeParams: true}액세스하려면 하위 라우터로 전달해야 params합니다.

mergeParamsExpress4.5.0 에 도입되었습니다 (2014 년 7 월 5 일)

이 예에서는 경로 에 itemRouter연결됩니다.userRouter/:userId/items

가능한 경로는 다음과 같습니다.

GET /user-> hello user
GET /user/5-> hello user 5
GET /user/5/items-> hello items from user 5
GET /user/5/items/6->hello item 6 from user 5

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

var userRouter = express.Router();
// you need to set mergeParams: true on the router,
// if you want to access params from the parent router
var itemRouter = express.Router({mergeParams: true});

// you can nest routers by attaching them as middleware:
userRouter.use('/:userId/items', itemRouter);

userRouter.route('/')
    .get(function (req, res) {
        res.status(200)
            .send('hello users');
    });

userRouter.route('/:userId')
    .get(function (req, res) {
        res.status(200)
            .send('hello user ' + req.params.userId);
    });

itemRouter.route('/')
    .get(function (req, res) {
        res.status(200)
            .send('hello items from user ' + req.params.userId);
    });

itemRouter.route('/:itemId')
    .get(function (req, res) {
        res.status(200)
            .send('hello item ' + req.params.itemId + ' from user ' + req.params.userId);
    });

app.use('/user', userRouter);

app.listen(3003);

3
답변 해주셔서 감사합니다. 여기서 사용하는 라우터는 Jordonias가 공유 한 라우터보다 더 명확하게 중첩됩니다. 그러나 그것은 후드 아래에서 동일하게 작동합니까? 포괄성에 대한 현상금을 부여하고 싶지만 몇 시간 후에는 할 수 없습니다.
— huggie

답변 해주셔서 감사합니다. 자식 경로에서 부모 경로의 쿼리 매개 변수 를 얻는 비슷한 방법이 있습니까?
— cwarny

1
쿼리 매개 변수가 특정 경로에 연결되어 있지 않기 때문에 어떤 경로에서도 사용할 수 없다면 놀랄 것입니다 ...
— Willem D' Haeseleer

매우 철저한 답변! 하나의 질문 : 사용자 라우터와 항목 라우터 사이의 지식 캡슐화 및 지식 분리를 위해 서브 라우터에 매개 변수가 필요하다는 것을 지정하는 선언적 방법이 있습니까? 즉, 항목 라우터가 사용자 ID를 전달할 것으로 예상하도록 알려주는 등록 또는 액세스 호출을 작성하는 명시적인 방법이 있습니까? 예 상황, 항목 라우터는 구조적으로 당신이 전화로 얻을하지 않는 한 사용자가 필요 명확하지 않으며 그것은 사용자 ID를 통과 할 것이라는 사용자의 라우터 만 분명, 완전히 다른 파일에
— yo.ian.g

이것은 라우터의 "표준"사용이 더 읽기 쉽지 않습니다. 코드를 볼 때 중첩을 시각화하는 방법을 찾고 있습니다.
— DrewInTheMountains

127

관리 가능한 중첩 경로 ...

Express 4에서 매우 관리 가능한 방식으로 중첩 된 경로를 수행하는 특정 예를 원했고 이것이 "표현 된 경로 중 가장 빠른"검색 결과입니다. 예를 들어 분해해야 할 경로가 많은 API가 있습니다.

./index.js :

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

// anything beginning with "/api" will go into this
app.use('/api', require('./routes/api'));

app.listen(3000);

./routes/api/index.js :

var router = require('express').Router();

// split up route handling
router.use('/products', require('./products'));
router.use('/categories', require('./categories'));
// etc.

module.exports = router;

./routes/api/products.js :

var router = require('express').Router();

// api/products
router.get('/', function(req, res) {
  res.json({ products: [] });
});

// api/products/:id
router.get('/:id', function(req, res) {
  res.json({ id: req.params.id });
});

module.exports = router;

폴더 구조의 중첩 예

"중첩 폴더 구조"에 대한 의견을 발견했습니다. 그러나 이것이 명확하지는 않기 때문에 아래 섹션을 추가했습니다. 다음 은 route 에 대한 중첩 폴더 구조 의 특정 예입니다 .

index.js
/api
  index.js
  /admin
    index.js
    /users
      index.js
      list.js
    /permissions
      index.js
      list.js

이것은 노드의 작동 방식에 대한 일반적인 예입니다. 디렉토리 기본값으로 웹 페이지에서 "index.html"이 작동하는 방식과 유사하게 폴더에서 "index.js"를 사용하는 경우 진입 점을 코드로 변경하지 않고 재귀에 따라 조직을 쉽게 확장 할 수 있습니다. "index.js"는 디렉토리에서 require 를 사용할 때 액세스되는 기본 문서 입니다.

index.js의 내용

const express = require('express');
const router = express.Router();
router.use('/api', require('./api'));
module.exports = router;

/api/index.js의 내용

const express = require('express');
const router = express.Router();
router.use('/admin', require('./admin'));
module.exports = router;

/api/admin/index.js의 내용

const express = require('express');
const router = express.Router();
router.use('/users', require('./users'));
router.use('/permissions', require('./permissions'));
module.exports = router;

/api/admin/users/index.js의 내용

const express = require('express');
const router = express.Router();
router.get('/', require('./list'));
module.exports = router;

여기에 일부 DRY 문제가있을 수 있지만 문제를 캡슐화하는 데 적합합니다.

참고로, 최근에 나는 actionhero에 들어가서 REST 패러다임을 뒤집는 진정한 프레임 워크 올인원과 같은 완전한 기능의 w / 소켓 및 작업 인 것을 발견했습니다. 당신은 아마 명시 적 / 알몸으로 그것을 확인해야합니다.


11
이것이 경로를 분할하는 방법을 보았지만 어떻게 중첩을 해결합니까?
— 1252748

완벽합니다 .... 그리고 말이됩니다. 이것은 확장 가능한 옵션입니다. op가 버전 관리 (v1, v2 등)를 어떻게 구현할 것인지 궁금합니다.
— Kermit_ice_tea

8
var userRouter = require('express').Router();
var itemRouter = require('express').Router({ mergeParams: true }); 

userRouter.route('/')
  .get(function(req, res) {})
  .post(function(req, res) {})
userRouter.route('/:user_id')
  .get(function() {})

itemRouter.route('/')
  .get(function(req, res) {})
  .post(function(req, res) {})
itemRouter.route('/:item_id')
  .get(function(req, res) {
    return res.send(req.params);
  });

app.use('/user/', userRouter);
app.use('/user/:user_id/item', itemRouter);

질문의 두 번째 부분의 핵심은 mergeParams 옵션을 사용하는 것입니다

var itemRouter = require('express').Router({ mergeParams: true }); 

에서 /user/jordan/item/catI reponse를 얻을 :

{"user_id":"jordan","item_id":"cat"}

멋있는. 당신과 Willem의 방법 모두 내가 원하는 것에 효과적입니다. 나는 그의 포괄 성을 점검 할 것이다. 그러나 나는 또한 당신을 표시 할 것이다. 고마워 귀하의 방법은 중첩되어 보이지 않지만 내가 원하는 것을 거의 좋아합니다. 감사.
— huggie

mergeParams 옵션이 여기에 중요합니다!
— MrE

2

@Jason Sebring 솔루션을 사용하고 Typescript에 적합합니다.

server.ts

import Routes from './api/routes';
app.use('/api/', Routes);

/api/routes/index.ts

import { Router } from 'express';
import HomeRoutes from './home';

const router = Router();

router.use('/', HomeRoutes);
// add other routes...

export default router;

/api/routes/home.ts

import { Request, Response, Router } from 'express';

const router = Router();

router.get('/', (req: Request, res: Response) => {
  res.json({
    message: 'Welcome to API',
  });
});

export default router;

당신은 제공 할 수 ./api/routes있습니까?
— Julian

1
@ 줄리안 : 파일 위치를 수정했습니다. ./api/routes두 파일을 가지고 index.ts와 home.ts. 첫 번째는에 의해 사용됩니다 server.ts. 그것이 도움이되기를 바랍니다.
— Pierre RA

0
try to add  { mergeParams: true } look to simple example  which it middlware use it in controller file getUser at the same for  postUser
    const userRouter = require("express").Router({ mergeParams: true });
    export default ()=>{
    userRouter
      .route("/")
      .get(getUser)
      .post(postUser);
    userRouter.route("/:user_id").get(function () {});
    
    
    }

-9

라우터는 하나만 필요하며 다음과 같이 사용하십시오.

router.get('/users');
router.get('/users/:user_id');

router.get('/users/:user_id/items');
router.get('/users/:user_id/items/:item_id');

app.use('api/v1', router);

예,하지만 항목과 사용자 사이의 논리를 분리하고 싶습니다. 따라서 분리하는 것을 선호합니다. 가능한지 모르겠습니다.
— huggie

@ huggie items는 users오른쪽 에 속하며 왜 분리해야합니까? 원하는 경우 동일한 라우터를 사용하여 다른 파일에서 파일을 정의 할 수 있습니다.
— eguneys

그것은 사용자에게 있지만 사용자에게 영향을 미치지 않고 쉽게 꽂거나 뺄 수 있기를 원합니다. 그리고 현재 다른 URL 끝점에 대한 각 라우터가 있습니다. 급행 발전기가 스타일을 장려하는 것 같습니다. 가능하지 않다면 라우터 인스턴스를 다른 파일로 보내야합니까? 그러나 그것은 원래 구조와 일치하지 않습니다.
— huggie

하나의 라우터를 다른 라우터 아래에 추가 할 수 있습니까? Express 미들웨어 아키텍처는 아래 라우터에 의해 처리되는 것처럼 보이므로 (전혀 있는지 확실하지는 않습니다) 가능하다고 생각합니다.
— huggie

2
-1 이것은 중첩 라우터에 관한 질문에 답하지 않습니다
— Willem D' Haeseleer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.