express 中文翻译 “快递”
- 基于http服务进行了二次封装
- 在express中扩展了很多 中间件
- 对路由进行了封装
- req,res 进行了一些功能的封装
关于express和koa中间件的区别
- koa 小巧,仅做基础封装
- express 有很多额外的中间件,路由
简单使用
需要安装npm install express
express.js
let express = require('../node_modules/express');
let app = express();
app.get('/',(req,res)=>{
res.end('ok'+req.money);
});
app.get('/hello', (req, res) => {
res.end('hello');
});
app.delete('/hello', (req, res) => {
res.end('delete hello');
});
app.post('/hello',(req,res)=>{
res.end('post hello');
});
app.all('*',(req,res)=>{
res.end('end');
});
// 发送静态文件
app.get('/404',function (req,res) {
res.sendFile('404.html',{root:__dirname});//必须保证绝对路径
//res.sendFile(require('path').join(__dirname,'404.html'));
})
app.listen(3000);
上述我们可以看出:
- express是一个函数
- 函数有 delete, post,all等,处理路由
- express执行后 返回的是一个监听函数
- 该函数可以起服务
- 在访问路由之前可以做很多事情 => 中间件
express架子 express/index.js
let http = require('http');
let url = require('url');
function createApplication(){
//若果请求方法,路径一致,则执行回调
let app = (req,res) => {
let reqMethod = req.method.toLowerCase();
let {pathname} = url.parse(req.url,true);
for(let i=0;i<app.routes.length;i++){
let {method,path,handler} = app.routes[i];
if(method === reqMethod && pathname == path){
handler(req,res)
}
}
res.statusCode = 404;
res.end(`Cannot ${reqMethod} ${pathname}`);
}
app.routes = [];
//存储 方法 路径 回调
app.get = (path,handler) => {
app.routes.push({
method:'get',
path,
handler
})
}
app.listen = (...args) => {
let server = http.createServer(app);
server.listen(...args);
}
return app;
}
module.exports = createApplication
post测试:可以下载postman插件下载安装
all
function createApplication(){
...
let app = (req,res) => {
...
for(let i=0;i<app.routes.length;i++){
let {method,path,handler} = app.routes[i];
if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){
handler(req,res)
}
}
res.statusCode = 404;
res.end(`Cannot ${reqMethod} ${pathname}`);
}
app.all = function (path, handler) {
app.routes.push({
method: 'all',
path,
handler
})
}
...
}
洋葱模型
express/test1.js
function app(){
}
app.routes = [];
app.use = function(cb){
app.routes.push(cb)
}
app.use((next)=> {
console.log(1);
next();
console.log(2);
})
app.use((next)=> {
console.log(3);
next();
console.log(4);
})
app.use((next)=> {
console.log(5);
console.log(6);
})
let index = 0;
function next(){
if(index === app.routes.lenth) return;
app.routes[index++](next)
}
next();
输出135642,这就是我们说的洋葱模型
中间件
- 中间件作用 当我们真正访问的路由之前可以干很多事情
- 可以决定是否向下执行
- 可以做一些权限判断,可以写多个中间件,和路由是放在同一个队列中的
- req是同一个
- 类似于洋葱模型
express.js
app.use('/', (req,res,next)=> {
req.money = 10000;
next();// 继续的意思,不调用next表示不继续了
});
app.use((req, res, next) =>{
req.money -= 2000;
next();
});
app.get('/',(req,res)=>{
res.end('ok'+req.money);
});
最后 localhost:3000 输出ok 3000
中间件实现 express/index.js
...
function createApplication(){
let app = (req,res) => {
let reqMethod = req.method.toLowerCase();
let {pathname} = url.parse(req.url,true);
let index = 0;
function next(){//将回调转换为next形式
if(index === app.routes.length){
res.statusCode = 404;
res.end(`Cannot ${reqMethod} ${pathname}`);
return;
}
let {method,path,handler} = app.routes[index++];
if(method === 'middleware'){
//中间件
if(pathname == path || path === '/' || pathname.startsWith(path + '/')){
handler(req,res,next)
}else{
next();// 没有迭代到 就执行下一个中间件
}
}else{
//路由
if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){
handler(req,res);
}else{
next();
}
}
}
next();
}
app.routes = [];
//存储 方法 路径 回调
let methods = ['get', 'post', 'put', 'delete', 'options'];
app.use = function(path,handler){
if(typeof handler != 'function'){
handler = path;
path = '/'
}
app.routes.push({
method: 'middleware',
path,
handler
})
}
...
return app;
}
module.exports = createApplication
有个特殊的地方,比如我们的路径是article/9/zdl,我们这样写article/:age/:name => {age:9,name:zdl}
如何解析这种路径?在express里可以通过req.params获取到
express.js
let express = require('../express');
let app = express();
app.get('/article/:id/:name', (req,res,next)=> {
console.log(req.params);
res.end(JSON.stringify(req.params));
});
app.listen(3000);
test: localhost:3000/:1/zdl1
express/index.js
...
function createApplication(){
let app = (req,res) => {
...
function next(){
...
if(method === 'middleware'){
...
}else{
//路由
if(path.params){
// 到路径参数的路由
if(path.test(pathname)){
let params = {}
let values = pathname.match(path).slice(1);
values.forEach((value,index)=>{
params[path.params[index]] = value
});
req.params = params; // 把参数挂载到req上
handler(req,res);
}else{
next();
}
}else{
...
}
}
}
next();
}
...
methods.forEach(method => {
app[method] = (path,handler) => {
//带路径参数的路由
let params = [];
if(path.includes(':')){//如果路径带有:就把路径转换成正则
path = path.replace(/:([^\/]+)/g,function(){
params.push(arguments[1]);
return '([^\/]+)';
})
path = new RegExp(path);
path.params = params;
}
app.routes.push({
method,
path,
handler
})
}
})
...
return app;
}
module.exports = createApplication
目录:

let http = require('http');
let url = require('url');
function createApplication(){
//若果请求方法,路径一致,则执行回调
let app = (req,res) => {
let reqMethod = req.method.toLowerCase();
let {pathname} = url.parse(req.url,true);
let index = 0;
function next(){
if(index === app.routes.length){
res.statusCode = 404;
res.end(`Cannot ${reqMethod} ${pathname}`);
return;
}
let {method,path,handler} = app.routes[index++];
if(method === 'middleware'){
//中间件
if(pathname === path || path == '/' || pathname.startsWith(path + '/')){
handler(req,res,next)
}else{
next();// 没有迭代到 就执行下一个中间件
}
}else{
//路由
if(path.params){
// 到路径参数的路由
if(path.test(pathname)){
let params = {}
let values = pathname.match(path).slice(1);
values.forEach((value,index)=>{
params[path.params[index]] = value
});
req.params = params; // 把参数挂载到req上
handler(req,res);
}else{
next();
}
}else{
if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){
handler(req,res);
}else{
next();
}
}
}
}
next();
}
app.routes = [];
//存储 方法 路径 回调
let methods = ['get', 'post', 'put', 'delete', 'options'];
app.use = function(path,handler){
if(typeof handler != 'function'){
handler = path;
path = '/'
}
app.routes.push({
method: 'middleware',
path,
handler
})
}
app.all = function (path, handler) {
app.routes.push({
method: 'all',
path,
handler
})
}
methods.forEach(method => {
app[method] = (path,handler) => {
//带路径参数的路由
let params = [];
if(path.includes(':')){//如果路径带有:就把路径转换成正则
path = path.replace(/:([^\/]+)/g,function(){
params.push(arguments[1]);
return '([^\/]+)';
})
path = new RegExp(path);
path.params = params;
}
app.routes.push({
method,
path,
handler
})
}
})
app.listen = (...args) => {
let server = http.createServer(app);
server.listen(...args);
}
return app;
}
module.exports = createApplication
中间件-1
express内置了很多中间件,把想要的方法已经解析好了,可以直接使用
let express = require('./express');
let app = express();
app.use(function (req,res,next) {
...
next();
});
app.get('/article', (req,res,next)=> {
console.log(req.path,req.query);
res.send({name:'zfpx'});
});
app.listen(3000);
实现 app.use的内容
test: localhost:3000/article?a=1&b=2
app.use(function (req,res,next) {
let {path,query} = url.parse(req.url,true);
req.path = path;
req.query = query;
req.hostname = 'XXXX';
//装饰模式
let end = res.end.bind(res);
res.send = function(value){
console.log(typeof value);
if(typeof value === 'object'){
end(JSON.stringify(value))
}else if(Buffer.isBuffer(value) || typeof value === 'string'){
end(value)
}else{
end(value.toString())
}
}
next();
});
中间件-2
启用一个静态服务
index.js
let express = require('./express');
let app = express();
app.use(function (req,res,next) {
...
next();
});
app.get('/article', (req,res,next)=> {
console.log(req.path,req.query);
res.send({name:'zfpx'});
});
app.listen(3000);
use内容
let express = require('express');
let app = express();
let path = require('path');
let fs = require('fs')
function static(p){
return (req ,res ,next) => {
let newPath = path.join(p, req.path);
fs.stat(newPath,(err,stat) => {
if(err){
next();
}else{
console.log(stat.isDirectory());
if(stat.isDirectory()){
let p = path.join(newPath,'/index.html');
console.log(p);
fs.createReadStream(p).pipe(res)
}else{
fs.createReadStream(newPath).pipe(res)
}
}
})
}
}