1206-express路由&参数获取&中间件 & 响应方法扩展

express nodejs Web应用开发框架

安装

  • npm install express ejs body-parser --save

使用

//引用express模块,他是一个函数
let express = require('express');
//express函数执行后,返回的是一个http的监听函数app,就是http.createServer中的函数
let app = express();
//在app函数上扩展了一个listen的方法,可以监听端口
app.listen(3000,()=>{
    console.log(`服务启动在3000端口`)
})

//=>剖析app.listen都做了什么? 启动了一个http服务,把监听函数传进去,并且监听了端口。
app.listen  = (...args)=>{
    require('http').createServer(this).listen(...args)
}
复制代码

主要功能:

  • 路由控制
  • 参数获取(req扩展的方法)
  • 中间件
  • send 和sendFile(res扩展的方法)
  • 静态文件服务
  • 模版解析
  • 重定向

主要功能分析

  • 路由控制 express-route.js
/*
    app监听函数上,扩展了很多方法,包括get post put...等,restful风格中的动词;
    app.all("*",cb) 表示匹配任何动词 任何路径
    app.方法名(路径,cb) => 匹配的是只是请求路径pathname,并且不包含?参数,而且是严格匹配;
    特点:从上到下匹配,如果匹配到了并且结束响应,就不会继续向下继续走了。

*/
let express = require('express');
let app = express();
app.listen(3000,()=>{console.log(`服务运行在3000端口`)})
app.get('/signin',(req,res)=>{
    res.setHeader('Content-Type','text/plain;charset=utf8')
    res.end('登录')
})
app.post('/signup',(req,res)=>{
    res.setHeader('Content-Type','text/plain;charset=utf8');
    res.end(post注册)
})
//all 表示所有动词方法, * 表示匹配任何路径。一般放到最后
app.all('*',(req,res)=>{
    res.end(404)
})
复制代码
  • 参数获取
/*
    req.query =>url.parse(req.url,true).query 取?参数
    req.path = >url.parse(req.url,true).pathname 取路径
    req.url //请求路径+?参数
    req.headers //是一个对象,key和value全部是小写的
    req.method //请求方法,全部是大写的
    req.params //路径参数,后续会讲到
    app.param('id',(req,res,next))根据路径参数拦截请求
*/


let express = require('express')
let app = express();
app.listen(3000)
app.get('/user',(req,res)=>{
    console.log(req.query.id)
    console.log(req.url)
    console.log(req.path)
    console.log(req.headers)
    console.log(req.method)
    if(req.query.id){
        //查一个用户
    }else{
        //查询所有用户
    }
})

--------
//利用路径参数,区分是查一个用户还是查所有用户
let express = require('express');
let app = express();
app.listen(3000)
app.get('/user',(req,res)=>{
    res.end('select All')
})
// /user/1/qiaoshi
//id表示占位符,必须有,但是可以随便写;
app.get('/user/:id/:name',(req,res)=>{
    console.log(res.params) // {id:1,name:qiaoshi}
    res.end(`select one ${req.params.id}${req.params.name}`)
})
//js实现 提取路径参数;
let url = '/user/1/xiaoming';
let path = '/user/:id/:name';
let keys = []
let newPath = path.replace(/:([^\/]+)/g,function(...args){
    keys.push(args[1])
    return '([^\/]+)';
})
let reg = new RegExp(newPath);
let values = reg.exec(url);
let params = {};
keys.forEach((key,index)=>{
    params[key] = values[index+1]
})
console.log(params)



---------
//利用app.param中间件,拦截路径参数,进行过滤,还可以进行其他操作
let express = require('express')
let app =express();
app.listen(3000)
app.param('id',(req,res,next)=>{
    req.params.id = `你的学号是${req.params.id}`
   //res.end(); ·
    next();//调用next,就继续向下匹配;
})
app.param('name',(req,res,next)=>{
    req.params.name = '你的名字是${req.params.name}'
    next()
})

app.get('/user/:id/:name',(req,res)=>{
    res.header('Content-Type','text/plain;charset=utf-8')
    res.end(req.params.id + req.params.name)
})
复制代码
  • 中间件,当我们访问到最终目标之前 需要执行的内容
/*
功能:1.可以进行权限判断
      2.可以对req res的属性进行扩充
      3.中间件放到最终目标钱穆庵
      4.中间件默认情况下,匹配路径'/';也可以指定匹配以什么开头的,以目录级数为单位;
      
    特殊:调用next传递了参数 =>错误中间件,四个参数=>  app.use((req,res,next,err)=>{console.log(err)})
          如果res.end,关闭了响应,在调用next,会继续匹配,但是响应已经关闭,返回给了客户端,后续的匹配毫无意义;
*/
let express =require('express');
let app = express();
app.listen(3000);
//app.use((req,res,next){})//不写路径,默认匹配'/',匹配任何路径;
app.use('/water',(req,res,next)=>{
    console.log('过滤石头');
    req.stone = 'too big'
    next();//不调用next,就不会继续向下走

})
app.use('/water',function(req,res,next)=>{
    console.log('过滤沙子');
    req.sand = ' too small'
    next();
})
app.use((req,res,next)=>{//利用中间件匹配所有,统一设置响应头编码和mime类型
    res.setHeader('Content-Type','text/plain;charset=utf8');
    next()
})
app.get('/water',(req,res)=>{//会过滤
    console.log('过滤完成')
    console.log(req.stone,req.sand)
    res.end('水')
})
app.get('/water/a',(req,res)=>{ //会过滤
    console.log('过滤完成')
    console.log(req.stone,req.sand)
    res.end('水')
})
app.get('/food',function (req, res) { //不会通过 /water的中间件进行过滤
    console.log(req.stone,req.sand) //undefined undefined
    res.end('食物')
})

//错误中间件,拥有四个参数,fn.length = 4; 
//触发:当前面的中间件 调用next传递了参数时,就认为报错了,就会执行错误中间件
app.use((err,req,res,next)=>{
    console.log(err)
})
复制代码
  • 中间件的装饰模式
//中间件装饰模式-就是在已有方法的基础上包装一层,生成新的方法,但不影响已有方法的功能,同时还扩展了自己的js逻辑
let express = require('express');
let app = express();
app.listen(3000)

app.use('/eat',(req,res,next)=>{
    let t = new Date().getTime();
    let end = res.end;
    res.end = (...args)=>{
        end.call(res,...args);
        console.log(new Date().getTime() - t)//计算每个接口请求的耗时
    }
})

app.get('/eat/water',(req,res)=>{
    for(let i = 0 ;i<10000;i++){
        
    }
    res.end('water')
})
app.get('/eat/food',(req,res)=>{
    for(let i = 0 ;i<10000000;i++){
        
    }
    res.end('food')
})
复制代码
  • js模拟express 中间件 next方法思路:
function app (){
    
}
//每次调用use方法,都会将方法存到数组中;
app.middleWares = []
app.use = function(cb){
    this.middleWares.push(cb)
}
app.use((req,res,next)=>{
    console.log(1)
    next()
})
app.use((req,res,next)=>{
    console.log(2)
    next()
})
app.use((req,res,next)=>{
    console.log(3)
})

//默认调用数组第一项,将next方法传递给数组中的方法;如果调用next 会继续执行数组中的下一项
let i = 0;
function next(){
    app.middleWares[i](null,null,next);
    i++;
}
next();
复制代码
  • res 扩展的方法 send sendFile json sendStatus
/*
res.json({name:1})  =>res.end(JSON.stringify({name:1})) 以前只能写入buffer和字符串,现在直接json,而且不用考虑utf8编码问题
res.sendStatus(200) = > res.statusCode = 200;res.end('ok') 
res.send() =>res.end() res.setHeader() //支持返回文本、json、状态吗等,最常用,不用考虑编码;
res.sendFile() => fs读取流然后pie;// 直接返回文件,不过要填绝对路径;
//sendFile不能通过../去查找上一级,root不支持。
//如果需要返回上一级的文件,需要用path模块拼接绝对路径;path.join(__dirnmae,'..','index.html)

//res.header('Content-Type','text/plain;charset=utf8'); =>res.setHeader() 设置响应头

*/

let express = require('express');
let app = express();
app.listen(3000);
app.get('/json',(req,res)=>{
    res.json({name:'小明'})
})
app.get('/',(req,res)=>{
    res.sendFile('./index.html',{root:__dirname});
    //等价于res.sendFile(__dirname+'/index.html')
    
    res.sendFile(require('path').join(__dirname,'..','index.html'))//上一级目录的文件
})
app.get('/status',(req,res)=>{
    res.sendStatus(200) //OK  404 =>not found
});

//js模拟扩展实现send方法
app.use(function(req,res,next){
  req.mySend = function(data){
    if(typeof data =='object'){
        res.setHeader('Content-Type','application/json;charset=utf8')
        res.end(JSON.stringify(data))
        return
    }
    if(typeof data =='string'){
        res.setHeader('Content-Type','text/plain;charset=utf8')
        res.end(data)
        return 
    }
    if(typeof data = 'number'){
        res.statusCode = data;
        let _http_server = require('_http_server');//
        res.end(_http_server.STATUS_CODES[data])//状态码信息映射表
        return
    }
    next()
  }  
})
app.get('/send',(req,res)=>{
    //res.send({name:'小明'})//send方法会自己判断传入的值是什么类型
    //res.send(200)
    //res.send('中文')
    
    res.mySend(404)
})


复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值