Express 框架

简介

Express 是一个简介灵活的 node.js Web应用框架。使用 Express 可以快速的搭建一个完整的功能网站。

Express 框架核心特性:

  1. 可以设置中间件来响应 HTTP 请求
  2. 定义了路由表用来执行不同的 HTTP 请求
  3. 可以通过向模版传递参数来动态渲染HTML也页面

安装 Express

# 新项目,需要初始化,生成package.json
npm init

# 安装express
sudo $ npm install express --save

看一个小实例

const express = require('express');
// 启用一个express的实例
const app = express();

// app.get 设置基础的路由 然后吐出数据
app.get('/', function(req, res) {
  res.send('Hello Express');
});

// app.listen一个端口 启动一个后台服务
const server = app.listen(8082, function() {
  console.log('接口已启动');
})

安装 supervisor

可以不用每次都手动关掉程序,再启动。这个会自动,只需要刷新即可。

$ npm install supervisor -g 

# 运行main.js
$ supervisor main.js

请求和响应

Request 常用的属性:

  • req.query: 获取url的查询参数串
  • req.params: 获取路由的parameters

Response 常用的属性:

  • res.redirect: 302, 重定向
  • res.json: 传送JSON响应
// http://localhost:8082/?name=kk&age=16
app.get('/', function (req, res) {
  // { name: 'kk', age: '16' }
  console.log(req.query);
});

// http://localhost:8082/22
app.get('/:id', function (req, res) {
  // 22
  console.log(req.params.id);
});

// http://localhost:8082/?name=kk&age=16
app.get('/', function(req, res) {
  // res.json(req.query);
  res.redirect('https://www.baidu.com');
});

静态文件

Express 提供了内置的中间件 express.static 来设置静态文件,如图片、css、script等。

创建一个小实例

// app.js
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
// 创建 application/x-www-form-urlencoded 编码解析
const urlencodedParser = bodyParser.urlencoded({ extended: false })

app.use(express.static('public'));

app.get('/index', function(req, res) {
  res.sendFile(__dirname + '/views/' + 'index.html');
});

app.post('/index', urlencodedParser, function(req, res) {
  console.log(req.body);
  res.redirect('https://www.baidu.com/s?wd='+ req.body.data + '&rsv_spt=1&rsv_iqid=0xfddb3e7d00091f5e&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_sug3=2&rsv_sug1=1&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=247&rsv_sug4=637');
});

app.listen(8082, function() {
  console.log('接口已启动');
});
<!-- views/index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>测试页面</title>
  <link rel="stylesheet" href="../css/index.css">
</head>
<body>
  Hello Express
  <form action="/index" method="post">
    <input type="text" name="data">
    <button type="submit">提交</button>
  </form>

  <script src="../scripts/index.js"></script>
</body>
</html>

使用中间件

中间件(Middleware)是一个函数,它可以访问请求对象,响应对象,和web应用中处于请求-响应循环流程中的中间件,一般命名为 next 的变量。

中间件的功能包括:

  • 执行任何代码
  • 修改请求和响应对象
  • 终结请求-响应循环
  • 调用堆栈中的下一个中间件

如果当前中间件没有终结请求-响应循环,就必须调用 next() 方法将控制权交给下一个中间件,不然请求就会被挂起, 会一直等待。

应用级中间件

应用级中间件绑定到 app对象 使用 app.use() 和 app.METHOD(), 其中 METHOD 是需要处理的http请求的方法。例如 get, post, put 等等。

// 应用级中间件
app.use(function(req, res, next) {
  console.log('没有写路由,如同*, 表示全部都拦截');
  next();
});

// 路由级中间件
app.use('/user', function(req, res, next) {
  console.log('拦截user路由');
  next();
});

app.get('/user/:id', function(req, res, next) {
  req.data = req.params.id;
  next();
}, function(req, res, next) {
  console.log(req.data);
  res.send('🆔为: ' + req.data);
});

路由级中间件

路由器级中间件与应用程序级中间件的工作方式相同,只不过它绑定到的实例express.Router()。

router.use() 和 router.METHOD() 函数家在路由级中间件。

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

const app = express();

router.use(function(req, res, next) {
  console.log('没有写路由,如同*, 表示全部都拦截');
  next();
});

router.use('/user', function(req, res, next) {
  console.log('拦截user路由');
  next();
});

router.get('/user/:id', function(req, res, next) {
  req.data = req.params.id;
  next();
}, function(req, res, next) {
  console.log(req.data);
  res.send('🆔为: ' + req.data);
});

// 在根路径使用路由中间件,只要经过'/', 都会使用
app.use('/', router);

app.listen('8082', function() {
  console.log('接口已启动');
});

错误处理中间件

错误处理中间件始终采用四个参数。您必须提供四个参数以将其标识为错误处理中间件函数。即使您不需要使用该next对象,也必须指定它以维护签名。否则,该next对象将被解释为常规中间件,并且将无法处理错误。

app.get('/user/', function (req, res, next) {
  consoleeeeeee.log(123);
});

app.use(function(error, req, res, next) {
  console.log(error.stack);
  res.status(500).send('Something broke!');
});

再来看看

app.get('/', function(req, res, next) {
  // 故意写错
  res.endss('Hello Express');
})

app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)

function logErrors (err, req, res, next) {
  console.error(err.stack)
  next(err)
}

// 在这种情况下,错误将明确传递给下一个错误
function clientErrorHandler (err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' })
  } else {
    next(err)
  }
}

function errorHandler (err, req, res, next) {
  // 设置编码格式
  res.writeHead(200,{'Content-Type': 'text/html;charset=utf-8'});
  res.status(500);
  // res.render('error', { error: err })
  res.end('啊哦😯, 程序小姐姐正在修复中... ');
}

实操代码

// 500.js
'use strict';
const http = require('http');

module.exports = function (template) {
  return function serverError(err, req, res, next) {
    if (!res.statusCode || res.statusCode !== 200) {
      res.statusCode = 500;
    }
    const desc = http.STATUS_CODES[res.statusCode];
    res.end(desc + '\n' + err);
    // next(err); 后面不再设置error handler, 不能再调用next, 否则会触发expressFinalHandler
  }
}

// 404.js
'use static';

module.exports = function (template) {
  return function fileNotFound (req, res, next) {
    const model = {
      url: req.url,
      statusCode: 404
    };

    if (req.xhr) {
      res.send(404, model);
    } else {
      res.status(404);
      res.render(template, req.data);
    }
  }
}

温馨提示

错误中间件有四个参数,第一个参数为error。
捕获到了错误,就会执行。
一般都都放在最后,作为兜底,但是像登录等例外。

扩展知识点:
日志记录库:log4js、express-log

内置中间件

从版本4.x开始,Express 不再依赖 Connect。以前包含的中间件功能现在位于单独的模块,详情查看中间件功能列表open in new window

目前 Express 具有以下内置的中间件功能:

  • express.static 不多介绍了,例子上面有
  • express.json 使用JSON负载解析传入的请求 v 4.16.0+ 可用
  • express.urlencoded 使用URL编码的有效内容解析传入的请求 v 4.16.0+ 可用
// 跟bodyParser.urlencoded 用法一致
app.use(express.urlencoded({ extended: false }));

app.post('/index', function (req, res) {
  console.log(req.body);
});

第三方中间件

使用第三方中间件向Express应用程序添加功能。

安装Node.js模块以获得所需的功能,然后在应用程序级别或路由器级别将其加载到您的应用程序中。

以下示例说明了如何安装和加载cookie解析中间件功能cookie-parser。

$ npm install cookie-parser -S
const cookieParser = require('cookie-parser');

app.use(cookieParser());
app.get('/user/', function (req, res, next) {
  console.log('🍊cookie: ', req.cookies);
  res.send(req.cookies['NX-ANTI-CSRF-TOKEN']);
});

更多第三方中间件,查阅列表open in new window

路由

路由是指如何定义应用的端口以及如何响应客户端的请求。

路由是由一个URL、HTTP请求(get、post 等)和若干个句柄组成。

app.METHOD(path, [callback...,]callback)

app.all() 是一个特殊的路由方法,不管是get、post、put 都支持。

// 正则匹配路径
app.get(/te(.+)t$/, function(req, res) {
  res.send('123');
});

// 多个回调,用next连接
const cb1 = function(req, res, next) {
  console.log('cb1');
  next();
}

const cb2 = function(req, res, next) {
  console.log('cb2');
  next();
}

app.all('/index', [cb1, cb2], function(req, res, next) {
  console.log('response will be sent by the next function...');
  next;
}, function(req, res) {
  res.send('end');
});

app.router()

app.router() 创建路由路径的链式路由句柄。由于路由在一个地方指定,这样做有利于创建模块坏的路由,而且减少代码冗余和拼写错误。

app.get('/index', function(req, res) {
  res.send('Hello Express')
});
app.post('/index', function(req, res) {
  res.send('Hello Express')
});
app.put('/index', function(req, res) {
  res.send('Hello Express')
});
app.delete('/index', function(req, res) {
  res.send('Hello Express')
});

// 上面那样写特别冗余,而且不好看,可以写成以下方式
app.route('/index')
  .get (function(req, res) {
    res.send('Hello Express')
  })
  .post (function(req, res) {
    res.send('Hello Express')
  })
  .put (function(req, res) {
    res.send('Hello Express')
  })
  .delete (function(req, res) {
    res.send('Hello Express')
  });

也可以直接用中间件 express.router();

使用模版引擎

需要在应用中进行如下设置才能让 Express 渲染模版文件:

  • views, 放模版文件的目录,比如:app.set('views', './views');
  • view engine, 引擎模版,比如: app.set('view engine', 'html');

推荐使用 swig 模版引擎,不推荐使用 jade

# 安装模版引擎
$ npm install swig --S

模版:直接拿到数据,可以直接渲染到页面上,不需要发送ajax请求。

// app.js
const express = require('express');
const swig = require('swig'); 

const app = express();

app.set('view cache', false);
app.set('view engine', 'html');
app.engine('html', swig.renderFile);

app.use(express.static('public'));
app.get('/', function (req, res) {
  res.render('index', {
    title: '测试首页',
    data: '这里是插入进来的内容呢😄'
  })
});

app.listen(8082, function() {
  console.log('接口已启动');
});
<!-- layout.html -->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{% block title %}{% endblock  %}</title>
  {% block head %}{% endblock  %}
</head>
<body>
  {% block content %}{% endblock  %}
</body>
</html>

<!-- index.html -->
{% extends "./layout.html" %}

{% block title %}
  index {{ title }}
{% endblock  %}

{% block head %}
<link rel="stylesheet" type="text/css" href="css/index.css">
{% endblock %}

{% block content %}
  <div>
    <h1>Hello Swig!</h1>
    <span>{{ data }}</span>
  </div>
{% endblock %}
Last Updated:
Contributors: kk