Node.js 를 사용 하여 간단 한 MVC 프레임 워 크 를 실현 하 는 방법
16810 단어 Node.jsMVC 프레임 워 크
간단 한 예시
먼저 간단 한 예제 에 착안 하여 Node 에서 클 라 이언 트 에 게 어떻게 동적 내용 을 되 돌려 주 는 지 알 수 있 습 니 다.
우리 가 이런 수요 가 있다 고 가정 하면:
사용자 가 방문
Node
할 때 남자 배우 목록 페이지 로 돌아 갑 니 다.사용자 가 방문
/actors
할 때 여배우 목록 으로 돌아 가기다음 코드 로 기능 을 완성 할 수 있 습 니 다:
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
const pathName = url.parse(req.url).pathname;
if (['/actors', '/actresses'].includes(pathName)) {
res.writeHead(200, {
'Content-Type': 'text/html'
});
const actors = ['Leonardo DiCaprio', 'Brad Pitt', 'Johnny Depp'];
const actresses = ['Jennifer Aniston', 'Scarlett Johansson', 'Kate Winslet'];
let lists = [];
if (pathName === '/actors') {
lists = actors;
} else {
lists = actresses;
}
const content = lists.reduce((template, item, index) => {
return template + `<p>No.${index+1} ${item}</p>`;
}, `<h1>${pathName.slice(1)}</h1>`);
res.end(content);
} else {
res.writeHead(404);
res.end('<h1>Requested page not found.</h1>')
}
}).listen(9527);
위의 코드 의 핵심 은 경로 가 일치 하 는 것 입 니 다.요청 이 도 착 했 을 때 경로 에 대응 하 는 논리 적 처리 가 있 는 지 확인 하고 요청 이 일치 하지 않 을 때 404 로 돌아 갑 니 다.일치 성공 시 해당 논 리 를 처리 합 니 다.위의 코드 는 분명히 통용 되 지 않 고 두 가지 경로 만 일치 하 는 대기 옵션(요청 방법 을 구분 하지 않 음)과 데이터 베이스 와 템 플 릿 파일 을 사용 하지 않 은 전제 에서 코드 는 이미 약간 꼬 였 다.따라서 그 다음 에 우 리 는 간단 하고 간단 한 MVC 구 조 를 구축 하여 데이터,모델,표현 을 분리 시 켜 각자 의 직책 을 맡 을 것 이다.
간이 MVC 프레임 워 크 구축
MVC 는 각각 다음 과 같다.
M:Model(데이터)
V:View(표현)
C:컨트롤 러(논리)
Node 에서 MVC 구조 에서 요청 을 처리 하 는 과정 은 다음 과 같 습 니 다.
서버 도착 요청
서버 에서 요청 을 경로 로 처리 합 니 다.
경로 일치,요청 가이드 대응 하 는 controller
controller 가 요청 을 받 고 model 에 데 이 터 를 요청 합 니 다.
model 은 controller 에 필요 한 데 이 터 를 되 돌려 줍 니 다.
controller 는 받 은 데 이 터 를 좀 더 가공 해 야 할 수도 있 습 니 다.
controller 는 처 리 된 데 이 터 를 view 에 건 네 줍 니 다.
view 데이터 와 템 플 릿 에 따라 응답 내용 생 성
서버 에서 이 내용 을 클 라 이언 트 로 되 돌려 줍 니 다.
이 를 근거 로 우 리 는 다음 과 같은 모듈 을 준비 해 야 한다.
server:감청 및 응답 요청
router:요청 을 올 바른 controller 에 맡 깁 니 다.
controllers:업무 논 리 를 실행 하고 model 에서 데 이 터 를 꺼 내 view 에 전달 합 니 다.
모델:데이터 제공
view:html 제공
다음 디 렉 터 리 만 들 기:
-- server.js
-- lib
-- router.js
-- views
-- controllers
-- models
serverserver.js 파일 생 성:
const http = require('http');
const router = require('./lib/router')();
router.get('/actors', (req, res) => {
res.end('Leonardo DiCaprio, Brad Pitt, Johnny Depp');
});
http.createServer(router).listen(9527, err => {
if (err) {
console.error(err);
console.info('Failed to start server');
} else {
console.info(`Server started`);
}
});
이 파일 의 세부 사항 에 관 계 없 이 router 는 다음 에 완 성 될 모듈 입 니 다.여기 서 먼저 도입 하고 도착 하면 바로 처리 하 십시오.라 우 터 모듈
router 모듈 은 사실 한 가지 일 만 완성 하면 정확 한 contrller 처 리 를 요청 합 니 다.이상 적 으로 이렇게 사용 할 수 있 습 니 다.
const router = require('./lib/router')();
const actorsController = require('./controllers/actors');
router.use((req, res, next) => {
console.info('New request arrived');
next()
});
router.get('/actors', (req, res) => {
actorsController.fetchList();
});
router.post('/actors/:name', (req, res) => {
actorsController.createNewActor();
});
전체적으로 말 하면,우 리 는 그것 이 중간 부품 과 비 중간 부품 을 동시에 지원 하 기 를 희망 하 며,요청 이 도착 하면 router 에서 일치 하 는 중간 부품 들 에 게 처리 할 것 입 니 다.미들웨어 는 요청 대상 과 응답 대상 에 접근 할 수 있 는 함수 로 미들웨어 에서 할 수 있 는 일 은 다음 과 같 습 니 다.로그 추가 와 처리 오류 등 모든 코드 를 실행 합 니 다.
요청(req)과 응답 대상(res)을 수정 합 니 다.예 를 들 어 req.url 에서 조회 파 라 메 터 를 가 져 오고 req.query 에 값 을 부여 합 니 다.
끝 응답
다음 미들웨어 호출(next)
Note:
주의해 야 할 것 은 특정한 미들웨어 에서 끝 응답 도 없고 next 방법 으로 제어 권 을 다음 미들웨어 에 맡 기지 않 으 면 요청 이 걸 립 니 다.
__비 경로 미들웨어다음 방식 으로 추가 합 니 다.모든 요청 과 일치 합 니 다.
router.use(fn);
예 를 들 어 위의 예:
router.use((req, res, next) => {
console.info('New request arrived');
next()
});
__루트 중간 부품다음 방식 으로 추가 합 니 다.요청 방법 과 경로 가 정확하게 일치 합 니 다.
router.HTTP_METHOD(path, fn)
빗질 을 다 한 후에 먼저 틀 을 써 라./lib/router.js
const METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'];
module.exports = () => {
const routes = [];
const router = (req, res) => {
};
router.use = (fn) => {
routes.push({
method: null,
path: null,
handler: fn
});
};
METHODS.forEach(item => {
const method = item.toLowerCase();
router[method] = (path, fn) => {
routes.push({
method,
path,
handler: fn
});
};
});
};
이상 은 주로 router 에 use,get,post 등 방법 을 추가 하고 이 방법 을 호출 할 때마다 routes 에 routes 규칙 을 추가 합 니 다.Note:
자바 script 에서 함 수 는 특수 한 대상 으로 호출 될 수 있 으 며 속성,방법 도 가 질 수 있 습 니 다.
다음 의 중점 은 router 함수 입 니 다.해 야 할 일 은:
req 대상 에서 method,pathname 가 져 오기
method,pathname 에 따라 routes 배열 의 각 route 와 추 가 된 순서대로 일치 하도록 요청 합 니 다.
어떤 route 와 일치 하 는 데 성공 하면 route.handler 를 실행 하고 다음 route 와 일치 하거나 끝 나 는 절 차 를 실행 합 니 다(다음 설명)
일치 하지 않 으 면 다음 route 와 계속 일치 하고 3,4 단 계 를 반복 합 니 다.
const router = (req, res) => {
const pathname = decodeURI(url.parse(req.url).pathname);
const method = req.method.toLowerCase();
let i = 0;
const next = () => {
route = routes[i++];
if (!route) return;
const routeForAllRequest = !route.method && !route.path;
if (routeForAllRequest || (route.method === method && pathname === route.path)) {
route.handler(req, res, next);
} else {
next();
}
}
next();
};
비 경로 미들웨어 에 대해 서 는 handler 를 직접 호출 합 니 다.경로 미들웨어 에 대해 서 는 요청 방법 과 경로 가 모두 일치 할 때 만 handler 를 호출 합 니 다.일치 하 는 route 가 없 을 때 다음 route 와 계속 일치 합 니 다.주의해 야 할 것 은 특정한 route 가 성공 적 으로 일치 하 는 상황 에서 handler 를 실행 한 후에 다음 route 와 일치 하 는 지 여 부 는 개발 자가 handler 에서 next()를 주동 적 으로 호출 하여 제어 권 을 내 놓 았 는 지 확인 해 야 합 니 다.
재server.js__일부 route 추가:
router.use((req, res, next) => {
console.info('New request arrived');
next()
});
router.get('/actors', (req, res) => {
res.end('Leonardo DiCaprio, Brad Pitt, Johnny Depp');
});
router.get('/actresses', (req, res) => {
res.end('Jennifer Aniston, Scarlett Johansson, Kate Winslet');
});
router.use((req, res, next) => {
res.statusCode = 404;
res.end();
});
요청 이 도착 할 때마다 먼저 log 를 인쇄 하고 다른 route 와 일치 합 니 다.actors 나 actresses 의 get 요청 과 일치 할 때 배우 이름 을 직접 보 내 고 다른 route 와 계속 일치 하지 않 아 도 됩 니 다.일치 하지 않 으 면 404 로 돌아 갑 니 다.브 라 우 저 에서 순서대로 접근http://localhost:9527/erwe、http://localhost:9527/actors、http://localhost:9527/actresses 테스트 해 보기:
/actresses
에서 관찰 한 결 과 는 기대 에 부합 되 고 배경 명령 행 에서 도 세 개의network
문 구 를 인쇄 했다.다음은 router 모듈 을 계속 개선 하 겠 습 니 다.
우선 router.all 방법 을 추가 합 니 다.호출 은 모든 요청 방법 에 route 를 추가 한 것 을 의미 합 니 다.
router.all = (path, fn) => {
METHODS.forEach(item => {
const method = item.toLowerCase();
router[method](path, fn);
})
};
이어서 오류 처 리 를 추가 합 니 다./lib/router.js
const defaultErrorHander = (err, req, res) => {
res.statusCode = 500;
res.end();
};
module.exports = (errorHander) => {
const routes = [];
const router = (req, res) => {
...
errorHander = errorHander || defaultErrorHander;
const next = (err) => {
if (err) return errorHander(err, req, res);
...
}
next();
};
server.js
...
const router = require('./lib/router')((err, req, res) => {
console.error(err);
res.statusCode = 500;
res.end(err.stack);
});
...
기본적으로 오류 가 발생 하면 500 을 되 돌려 주지 만 개발 자가 router 모듈 을 사용 할 때 자신의 오류 처리 함수 로 대체 할 수 있 습 니 다.코드 를 수정 하여 오류 처 리 를 정확하게 수행 할 수 있 는 지 테스트 합 니 다.
router.use((req, res, next) => {
console.info('New request arrived');
next(new Error('an error'));
});
이렇게 하면 모든 요청 이 500 으로 돌아 가 야 합 니 다.계속,route.path 와 pathname 의 일치 규칙 을 수정 합 니 다.현재 우 리 는 두 문자열 이 같 을 때 만 일치 하 게 통과 할 수 있다 고 생각 합 니 다.이것 은 url 에 경로 매개 변 수 를 포함 하 는 상황 을 고려 하지 않 았 습 니 다.예 를 들 어:
localhost:9527/actors/Leonardo
...과router.get('/actors/:name', someRouteHandler);
이 route 는 성공 적 으로 일치 해 야 합 니 다.문자열 형식의 route.path 를 정규 대상 으로 변환 하고 route.pattern 에 저장 하 는 함수 가 추가 되 었 습 니 다.
const getRoutePattern = pathname => {
pathname = '^' + pathname.replace(/(\:\w+)/g, '\(\[a-zA-Z0-9-\]\+\\s\)') + '$';
return new RegExp(pathname);
};
이렇게 하면 경로 매개 변 수 를 가 진 url 과 일치 하고 이 경로 매개 변 수 를 req.params 대상 에 저장 할 수 있 습 니 다.
const matchedResults = pathname.match(route.pattern);
if (route.method === method && matchedResults) {
addParamsToRequest(req, route.path, matchedResults);
route.handler(req, res, next);
} else {
next();
}
const addParamsToRequest = (req, routePath, matchedResults) => {
req.params = {};
let urlParameterNames = routePath.match(/:(\w+)/g);
if (urlParameterNames) {
for (let i=0; i < urlParameterNames.length; i++) {
req.params[urlParameterNames[i].slice(1)] = matchedResults[i + 1];
}
}
}
route 테스트 추가:
router.get('/actors/:year/:country', (req, res) => {
res.end(`year: ${req.params.year} country: ${req.params.country}`);
});
방문New request arrived
해 보기:router 모듈 은 여기에 쓰 여 있 습 니 다.매개 변수의 포맷 과 요청 주 체 를 가 져 오 는 것 에 대해 서 는 사소한 것 은 시험 하지 않 고 bordy-parser 등 모듈 을 직접 사용 할 수 있어 야 합 니 다.
현재 우 리 는 router 모듈 을 만 들 었 습 니 다.그 다음 에 route handler 안의 업무 논 리 를 모두 contrller 로 옮 깁 니 다.
수정server.js__,컨트롤 러 도입:
...
const actorsController = require('./controllers/actors');
...
router.get('/actors', (req, res) => {
actorsController.getList(req, res);
});
router.get('/actors/:name', (req, res) => {
actorsController.getActorByName(req, res);
});
router.get('/actors/:year/:country', (req, res) => {
actorsController.getActorsByYearAndCountry(req, res);
});
...
새controllers/actors.js__:
const actorsTemplate = require('../views/actors-list');
const actorsModel = require('../models/actors');
exports.getList = (req, res) => {
const data = actorsModel.getList();
const htmlStr = actorsTemplate.build(data);
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end(htmlStr);
};
exports.getActorByName = (req, res) => {
const data = actorsModel.getActorByName(req.params.name);
const htmlStr = actorsTemplate.build(data);
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end(htmlStr);
};
exports.getActorsByYearAndCountry = (req, res) => {
const data = actorsModel.getActorsByYearAndCountry(req.params.year, req.params.country);
const htmlStr = actorsTemplate.build(data);
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end(htmlStr);
};
controller 에 view 와 model 을 동시에 도입 하여 이 두 사람 간 의 접착제 역할 을 한다.controller 의 작업 돌 이 켜 보기:controller 가 요청 을 받 고 model 에 데 이 터 를 요청 합 니 다.
model 은 controller 에 필요 한 데 이 터 를 되 돌려 줍 니 다.
controller 는 받 은 데 이 터 를 좀 더 가공 해 야 할 수도 있 습 니 다.
controller 는 처 리 된 데 이 터 를 view 에 건 네 줍 니 다.
이 controller 에서 저 희 는 model 모듈 의 방법 으로 배우 목록 을 가 져 오고 데 이 터 를 view 에 건 네 주 며 view 에 게 배우 목록 페이지 를 보 여 주 는 html 문자열 을 생 성 합 니 다.마지막 으로 이 문자열 을 클 라 이언 트 에 게 되 돌려 주 고 브 라 우 저 에 목록 을 보 여 줍 니 다.
model 에서 데이터 가 져 오기
보통 model 은 데이터 베 이 스 를 통 해 데 이 터 를 가 져 와 야 합 니 다.여기 서 우 리 는 데 이 터 를 json 파일 에 저장 하 는 것 을 간소화 합 니 다.
/models/test-data.json
[
{
"name": "Leonardo DiCaprio",
"birth year": 1974,
"country": "US",
"movies": ["Titanic", "The Revenant", "Inception"]
},
{
"name": "Brad Pitt",
"birth year": 1963,
"country": "US",
"movies": ["Fight Club", "Inglourious Basterd", "Mr. & Mrs. Smith"]
},
{
"name": "Johnny Depp",
"birth year": 1963,
"country": "US",
"movies": ["Edward Scissorhands", "Black Mass", "The Lone Ranger"]
}
]
이어서 모델 에서 이 데이터 에 접근 할 수 있 는 방법 을 정의 할 수 있다.models/actors.js
const actors = require('./test-data');
exports.getList = () => actors;
exports.getActorByName = (name) => actors.filter(actor => {
return actor.name == name;
});
exports.getActorsByYearAndCountry = (year, country) => actors.filter(actor => {
return actor["birth year"] == year && actor.country == country;
});
controller 가 model 에서 원 하 는 데 이 터 를 얻 으 면 다음 단 계 는 view 가 빛 을 발 하고 열 을 낼 차례 입 니 다.view 층 은 보통 템 플 릿 엔진,예 를 들 어 dust 등 을 사용 합 니 다.마찬가지 로 간소화 하기 위해 서 여 기 는 템 플 릿 에서 자 리 를 차지 하 는 부 호 를 간단하게 교체 하 는 방식 으로 html 를 얻 고 매우 제한 적 으로 과장 하 며 과정 을 대충 이해 하면 됩 니 다.생 성/views/actors-list.js:
const actorTemplate = `
<h1>{name}</h1>
<p><em>Born: </em>{contry}, {year}</p>
<ul>{movies}</ul>
`;
exports.build = list => {
let content = '';
list.forEach(actor => {
content += actorTemplate.replace('{name}', actor.name)
.replace('{contry}', actor.country)
.replace('{year}', actor["birth year"])
.replace('{movies}', actor.movies.reduce((moviesHTML, movieName) => {
return moviesHTML + `<li>${movieName}</li>`
}, ''));
});
return content;
};
브 라 우 저 에서 테스트 해 보기:이로써 큰 성 과 를 거 두 었 다!
이상 의 Node.js 를 사용 하여 간단 한 MVC 프레임 워 크 를 실현 하 는 방법 은 바로 편집장 이 여러분 에 게 공유 한 모든 내용 입 니 다.여러분 에 게 참고 가 되 고 여러분 들 이 저 희 를 많이 응원 해 주시 기 바 랍 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Grunt에서 즉석 템플릿 엔진이 필요했기 때문에 마침내 만들었습니다.jade 라든지 ejs 라고도 좋지만, 보다 심플하게 하고 싶다고 생각해. json을 떨어 뜨렸다. 플레이스홀더(:TAG)를 마련해, 이런 HTML 만들어 둔다. template.html 자리 표시자의 문자열을 키로...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.