Nodejs学习(四) --- express

express

  • express是nodejs最常用的web server 框架

安装express

安装脚手架

npm i express-generator -g

创建项目

express blog_express
cd blog-express
npm i 
npm start

配置package.json

npm i nodemon cross-env -D
 "scripts": {
    "start": "node ./bin/www", 
    "dev":"cross-env NODE_ENV=development nodemon ./bin/www"
  },

介绍app.js

var createError = require('http-errors'); 对错误页面的处理
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser'); 对cookie的解析 通过req.cookies就可以直接访问cookie
var logger = require('morgan');  记录日志

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev')); 使用logger日志
app.use(express.json()); 可以获取到post json的数据 req.body获取
app.use(express.urlencoded({ extended: false })); 获取post除了json之外格式的数据 都是绑定到req.body上
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); 

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;


express-router
// app.js
app.use('/api/blog', blogRouter);
app.use('/api/user', userRouter);

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

router.post('/login', function(req, res, next) {
  const {username,password} = req.body;
  res.json({
    errno:0,
    data:{
      username,
      password
    }
  })
});
router.get('/detail', function(req, res, next) {
  res.json({
    errno:0,
    data:'ok'
  })
});
  • post请求的数据在req.body
  • get请求的数据在req.query

express-中间件

在 Node.js 中被广泛使用,它泛指一种特定的设计模式、一系列的处理单元、过滤器和处理程序,以函数的形式存在,连接在一起,形成一个异步队列,来完成对任何数据的预处理和后处理。

其实中间件就是请求req和响应res之间的一个应用,请求浏览器向服务器发送一个请求后,服务器直接通过request定位属性的方式得到通过request携带过去的数据,就是用户输入的数据和浏览器本身的数据信息,这中间就一定有一个函数将这些数据分类做了处理,最后让request对象调用使用,这个或者多个的处理函数就是我们所说得中间件。

原理分析
  • app.use用来注册中间件,先收集起来。
  • 遇到http请求,根据path和method判断触发那些。
  • 实现next机制,即上一个通过next触发下一个
实现
  • 创建存放所有路由的对象(all,get,post.。。。),后期对不同的请求处理都会放到这里
  • 声明对应处理的函数use(),get(),post(),register函数统一处理path信息,返回{path:'xxx',stack:['每个path对应中间件函数数组']}
  • use(),get(),post(),将自己的info信息存储到 this.routes
  • listen监听端口
  • callbackcreateServer回调,获取当前url的路由和method
  • match 返回当前url所需要执行的所有中间件集合
  • handle核心方法 实现 next()调用各个中间件
const http = require('http');
const slice = Array.prototype.slice;

class LikeExpress{
  constructor(){
    // 静态属性
    this.routes = {
      all:[],
      get:[],
      post:[]
    }
  }
  // 原型上定义的函数
  
  //中间件公用的地方 定义在register里面
  register(path){
    const info = {};
    if(typeof path === 'string'){
      //如果第一个参数是路由的话
      info.path = path;
      // stack 以数组的形式存放路由后面的中间件
      info.stack = slice.call(arguments,1);
    }else{
      // 如果是app.use((req,res,next)=>{}) 没有第一个参数的形式
      info.path = '/';
      info.stack = slice.call(arguments,0);
    }
    console.log(`info is ${info}`);
    return info;
  }

  use(){
    const info = this.register.apply(this,arguments);
    this.routes.all.push(info);
  }
  get(){
    const info = this.register.apply(this,arguments);
    this.routes.get.push(info);
  }
  post(){
    const info = this.register.apply(this,arguments);
    this.routes.post.push(info);
  }

  // 监听事件
  listen(...args){
   
    const server = http.createServer(this.callback());
    server.listen(...args);
  }
  // 定义server的回调函数
  callback(){
    return (req,res)=>{
      // 定义res.json的使用
      res.json = (data)=>{
        res.setHeader('Content-type','application/json');
        res.end(
          JSON.stringify(data)
        )
      }

      const url = req.url;
      const method = req.method.toLowerCase();

      const resultList = this.match(method,url);
      this.handle(req, res, resultList)
    }
  }

  // match 当前url所需要执行的所以中间件集合
  match(method,url){
    let stack = [];
    if(url === '/favicon.ico'){
      return stack;
    }

    // 获取routes
    let curRoutes =[];
    // 当前路由匹配到的中间件:所有的 all 以及当前url的method对应所有中间件
    curRoutes = curRoutes.concat(this.routes.all);
    curRoutes = curRoutes.concat(this.routes[method]);
    
    curRoutes.forEach(routeInfo =>{
      if(url.indexOf(routeInfo.path) === 0){
        // url === '/api/get-cookie' 且 routeInfo.path === '/'
        // url === '/api/get-cookie' 且 routeInfo.path === '/api'
        // url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'
       
        // 当前路由在curRoutes 中匹配对应自己的所有中间件
        stack = stack.concat(routeInfo.stack)
      }
    })

    return stack
    
  }

  // 核心的 next机制
  handle(req,res,stack){
    const next = ()=>{
      const middleware = stack.shift();
      if(middleware){
        middleware(req, res, next);
      }
    }
    next();
  }
}

module.exports = () => {
  return new LikeExpress()
}
调用
const express = require('./like-express2');

const app = express();

app.use((req,res,next)=>{
  console.log('请求开始....',req.method,req.url);
  next();
})

app.use((req,res,next)=>{
  console.log('处理 cookie');
  req.cookie = {
    userId:'abc123'
  }
  next();
})

app.use('/api',(req,res,next)=>{
  console.log('处理 /api 路由');
  next();
})

app.get('/api',(req,res,next)=>{
  console.log('get 处理 /api 路由');
  next();
})

// 模拟登录验证

function loginCheck(req,res,next){
  setTimeout(() => {
    console.log('模拟登录成功'); 
    next();
  });
}

app.get('/api/get-cookie',loginCheck,(req,res,next)=>{
  console.log('get 处理 /api/get-cookie 路由');
  res.json({
    errno:0,
    data:req.cookie
  }) 
})

app.listen(8000,()=>{
  console.log(`server listen running http://localhost:8000`);
})

//访问 http://localhost:8000/api/get-cookie

//请求开始.... GET /api/get-cookie
//处理 cookie
//处理 /api 路由
//get 处理 /api 路由
//模拟登录成功
//get 处理 /api/get-cookie 路由

morgan日志
' dev 开发模式  combined线上模式 '

const ENV = process.env.NODE_ENV
if(ENV !== 'production'){
  // 开发环境
  app.use(logger('dev',{
    stream:process.stdout //默认值 
  }));
}else{
  // 线上环境
  const logFileName = path.join(__dirname,'logs','access.log');
  const writeStream = fs.createWriteStream(logFileName,{
    flags:'a'
  })
  app.use(logger('combined',{
    stream:writeStream
  }));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值