express入门总结

1.Express框架

timg.jpg

1.1 express是什么?

创建http服务器特别麻烦,express框架解决了这个的问题。

Express在node界的地位,就相当于jQuery在DOM界的地位。Express 是一个自身功能极简,完全是由路由和中间件构成一个的 web 开发框架:从本质上来说,一个 Express 应用就是在调用各种中间件。

image

中文官网(实际上也是英语的):http://www.expressjs.com.cn/

官方的概念为:基于 Node.js 平台,快速、开放、极简的 web 开发框架。

1.2 express的安装和使用、

安装方式为:

npm install express --save //局部
npm install express --g //全局

express框架的API非常简单,如下:

var express = require("express"); //返回的是一个函数
var app = express();        //创建app,express函数执行返回一个app对象

//当用户get请求访问/的时候
app.get("/", (req,res)=>{
    //输出可用end()或send()方法,而且不用设置字符集,因为express封装好了
    res.send("<h1>首页</h1>");
})

//当用户get请求访问/music的时候
app.get("/music", (req,res)=>{
    res.send("<h1>音乐频道</h1>");
})

app.listen(3000, function(err){
    console.log("3000端口")
})

express开发项目可以使用express应用程序生成器:

安装:全局安装express-generator

npm install -g express-generator

创建:在想要的文件夹位置运行命令,创建一个名为 myapp 的 express 应用程序

express --view=pug myapp

成功后会自动在目标位置创建一个名为 myapp 的项目并生成很多文件,命令行显示效果如下:


image.png

执行命令:在命令行最下面有提示:

1、进入项目文件夹:cd myapp
2、安装依赖:npm install
3、启动:npm start

查看:在浏览器输入localhost:3000,会打开一个页面显示Welcome to Express,说明已经成功启动服务了。至此。一个express项目完成。


image.png

1.3 express的项目结构

着你的应用发展到一定的大小和复杂时,事情可能就会变得令人困惑了。你的代码太乱了。随着你团队的成长,在原来代码基础上继续工作将变得很困难。每当代码合并时,你都要和冲突进行“斗争”。添加新的特性和不断地处理新的情况的话,需要改变应用的结构。而且,有如此多不同的方法来组织你的文件你和代码,并且在这些方法中很难选择出最适合你的。,为了方便后期的开发,一个express项目需要一个合理的项目结构:

├── app.js                //项目的入口文件
├── bin                     //存放项目启动的执行文件           
│   └── www
├── node_modules  //安装的依赖
├── package.json    //项目的依赖配置与版本信息
├── public                //存放静态文件的目录
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes                //路由文件,默认创建的express项目包括index.js和user.js
│   ├── index.js
│   └── users.js
└── views                 //视图文件,保存模板引擎中的模板
    ├── error.pug
    ├── index.pug
    └── layout.pug

2. 应用 Application

2.1 app.use()和app.get()的区别

回调函数不同

app.use(path,callback)中的callback既可以是router对象又可以是函数
app.get(path,callback)中的callback只能是函数

当一个路由有好多个子路由时用app.use(path,router)
例子:
http://localhost:3000/home/one
http://localhost:3000/home/second
http://localhost:3000/home/three
路由/home后面有三个子路由紧紧跟随,分别是/one,/second,/three
如果使用app.get(),则要不断的重复,很麻烦,也不利用区分

app.get("/home",callback)
app.get("/home/one",callback)
app.get("/home/second",callback)
app.get("/home/three",callback)

我们可以创建一个router.js 专门用来一个路由匹配多个子路由

var express = require('express')
var router = express.Router()
router.get("/",(req,res)=>{
    res.send("/")
})
router.get("/one",(req,res)=>{
    res.send("one")
})
router.get("/second",(req,res)=>{
    res.send("second")
})
router.get("/treen",(req,res)=>{
    res.send("treen")
})
module.exports = router;

在app.js中导入router.js

var express = require('express')
var router = require("./router")
var app = express()

app.use('/home',router) //router路由对象中的路由都会匹配到"/home"路由后面
app.get('/about', function (req, res) {
  console.log(req.query)
  res.send('你好,我是 Express!')
})

// 4 .启动服务
app.listen(3000, function () {
  console.log('app is running at port 3000.')
})

什么时用app.use,什么时用app.get呢?

路由规则是app.use(path,router)定义的,router代表一个由express.Router()创建的对象,在路由对象中可定义多个路由规则。可是如果我们的路由只有一条规则时,可直接接一个回调作为简写,也可直接使用app.getapp.post方法。即
当一个路径有多个匹配规则时,使用app.use()

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

为了提供对静态资源文件(图片,css,js文件)的服务,请使用Express内置的中间函数express.static.

传递一个包含静态资源的目录给express.static中间件用于立即开始提供文件。 比如用以下代码来提供public目录下的图片、css文件和js文件:
app.use(express.static('public'));

如果前台想请求后台public目录下images/08.jpg静态的图片资源
通过: http://localhost:3000/images/08.jpg

通过多次使用 express.static中间件来添加多个静态资源目录:

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

Express将会按照你设置静态资源目录的顺序来查看静态资源文件。

为了给静态资源文件创建一个虚拟的文件前缀(文件系统中不存在),可以使用express.static函数指定一个虚拟的静态目录,如下:

app.use('/static', express.static('public'))

现在你可以使用‘/static’作为前缀来加载public文件夹下的文件了

比如: http:// localhost:3000/static/image/kitten.jpg

2.2 express路由

2.2.1基本路由

通常HTTP URL的格式是这样的:

http://host[:port][path]

http表示协议。
host表示主机。
port为端口,可选字段,不提供时默认为80。
path指定请求资源的URI(Uniform Resource Identifier,统一资源定位符),如果URL中没有给出path,一般会默认成“/”(通常由浏览器或其它HTTP客户端完成补充上)。

所谓路由,就是如何处理HTTP请求中的路径部分。比如“http://xxx.com/users/profile”这个URL,路由将决定怎么处理/users/profile这个路径。

还是先直接看一个demo:基本的路由示例

const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('hello, express')
})

app.get('/users/:name', function (req, res) {
  res.send('hello, ' + req.params.name)
})

app.listen(3000)

在上面的demo中,当我们访问'/'这个路径的时候,服务端响应返回一个'hello express',其实这就是一个express基本的路由。

express路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成,它的结构如下: app.method(path, [callback...], callback), app是 express对象的一个实例,method是一个 HTTP 请求方法, path是服务器上的路径, callback是当路由匹配时要执行的函数。

下面展示一个post请求的路由

app.post('/', function (req, res) {
  res.send('POST request to the homepage');
});
2.2.2 路由句柄

处理特定路由的回调函数叫做路由句柄,路由句柄行为类似中间件,例如:
使用一个回调函数处理路由:

app.get('/example/a', function (req, res) {
  res.send('Hello from A!');
});

使用多个回调函数处理路由

//需要指定 next 对象
app.get('/example/b', function (req, res, next) {
  console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});

使用回调函数数组处理路由

var cb0 = function (req, res, next) {
  console.log('CB0');
  next();
}
var cb1 = function (req, res, next) {
  console.log('CB1');
  next();
}
var cb2 = function (req, res) {
  res.send('Hello from C!');
}
app.get('/example/c', [cb0, cb1, cb2]); 

express 要路由到相应的处理函数,需要method和path两个都满足条件。

(1) method就是'get'、'post'等,根据这个来找到map中相应的属性

(2)然后在数组中,检查path是否符合里面的Route的要求,如果符合,相应的callback函数会依次(看后面的解释)执行。callback函数的签名是:

function(req,res,next){}

如果一个callback没有结束一个请求响应的生命周期( 比如写下res.end('...') ),那么一般需要在最后写上next(),像下面那样

function(req,res,next){
    //..自己的业务逻辑
    next();
}

这样,就会继续检查下面的一个Route
如果callback中,会终结一个请求响应周期(一般也是这样),那么就算有next(),也不会继续检查下一个Route

//一般路由函数都应该和这个类似
function(req,res,next){
    //..自己的业务逻辑
    res.end('...');//或res.render('...')等
}

req.query: 解析后的 url 中的querystring,如?name=haha,req.query 的值为{name: 'haha'}
req.params: 解析 url 中的占位符,如/:name,访问/haha,req.params 的值为{name: 'haha'}
req.body: 解析后请求体,需使用相关的模块,如 body-parser,请求体为{"name": "haha"},则 req.body 为 {name: 'haha'}
app.get:('/',function(req,res))获得get请求
app.post:('/',function(req,res))获得post请求
app.all:('/',function(req,res))获得所有(包括get和post)请求

2.2.3 路由模块

router目的是将路由进行进一步的划分。
可以将其视为“迷你应用程序”,只能执行中间件和路由功能。每个Express应用程序都有一个内置的应用程序路由器。

顶级express对象具有Router()创建新router对象的功能。

var router = express.Router([options]);

可选options参数指定路由器的行为。

属性 描述 默认
caseSensitive 启用区分大小写 默认情况下禁用,将“/ Foo”和“/ foo”视为相同。
mergeParams 保留req.params父路由器的值。如果父项和子项具有冲突的参数名称,则子项的值优先。 false
strict 启用严格路由。 默认情况下禁用,“/ foo”和“/ foo /”由路由器处理相同。

其实我们可以将router视为一个路由分支,因此router是有中间件和HTTP方法路由(如get,put,post,等),router就像一个应用程序。

之前我们的代码都是写在了app.js中,不利于阅读维护,多人开发时肯定问题繁多,所以我们需要将路由模块化。
如果我们的功能主要有登录,商品管理,那么主要的模块就是登录和商品

image

所以在路由目录就有登录和商品两部分的路由
login.js

const express = require("express");
const router = express.Router();
router.get("/",function (req,res) {
    res.send("登录页面")
});
router.get("doLogin",function (req,res) {

});
module.exports = router;

product.js

const express = require("express");
const router = express.Router();
router.get("/",function (req,res) {
    res.send("商品首页")
});
router.get("/add",function (req,res) {
    res.send("商品首页")
});
router.get("/delete",function (req,res) {
    res.send("商品首页")
});
module.exports = router;

admin.js
这两部分放到一个公共的路由文件中

const express = require("express");
const router = express.Router();
//相当于后台的路由,所有的后台处理都需要从这里经过

const login = require("admin/login");
const product = require("admin/product");

router.use("/login",login);
router.use("/product",product);

module.exports = router;

在app.js中导入

const express = require("express");
const app = new express();
//引入route模块
const admin  = require("./route/admin");
//加载admin模块
app.use("/admin",admin);
app.listen("3001","127.0.0.1");

2.3 中间件

Express里有个中间件(middleware)的概念。所谓中间件,就是在收到请求后和发送响应之前这个阶段执行的一些函数。

要在一条路由的处理链上插入中间件,可以使用express对象的use方法。该方法原型如下:

app.use([path,] function [, function...])

当app.use没有提供path参数时,路径默认为“/”。当你为某个路径安装了中间件,则当以该路径为基础的路径被访问时,都会应用该中间件。比如你为“/abcd”设置了中间件,那么“/abcd/xxx”被访问时也会应用该中间件。
中间件函数的原型如下:

function (req, res, next)

第一个参数是Request对象req。第二个参数是Response对象res。第三个则是用来驱动中间件调用链的函数next,如果你想让后面的中间件继续处理请求,就需要调用next方法。

给某个路径应用中间件函数的典型调用是这样的:

app.use('/abcd', function (req, res, next) {
  console.log(req.baseUrl);
  next();
})

中间件可以分成几类:

(1)应用级中间件
var app = express();

// 没有挂载路径的中间件,应用的每个请求都会先执行该中间件,next()会让其进入下一个中间件
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它,next()后接着进入下一个中间件
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

一个中间件执行完需要使用next()或者end()函数将执行权交给下一个中间件,否则之后的中间件将不会执行。

(2) app.static中间件

Express提供了一个static中间件,可以用来处理网站里的静态文件的GET请求,可以通过express.static访问。

express.static的用法如下:

express.static(root, [options])

第一个参数root,是要处理的静态资源的根目录,可以是绝对路径,也可以是相对路径。第二个可选参数用来指定一些选项,比如maxAge、lastModified等,更多选项的介绍看这里:http://expressjs.com/guide/using-middleware.html#middleware.built-in

一个典型的express.static应用如下:

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

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

上面这段代码将当前路径下的public目录作为静态文件,并且为Cache-Control头部的max-age选项为1天。还有其它一些属性,请对照express.static的文档来理解。

使用express创建的HelloExpress项目的app.js文件里有这样一行代码:

app.use(express.static(path.join(__dirname, 'public')));

这行代码将HelloExpress目录下的public目录作为静态文件交给static中间件来处理,对应的HTTP URI为“/”。path是一个Node.js模块,__dirname是Node.js的全局变量,指向当前运行的js脚本所在的目录。path.join()则用来拼接目录。

有了上面的代码,你就可以在浏览器里访问“http://localhost:3000/stylesheets/style.css”。我们做一点改动,把上面的代码修改成下面这样:

app.use('/static', express.static(path.join(__dirname, 'public')));
上面的代码呢,针对/static路径使用static中间件处理public目录。这时你再用浏览器访问“http://localhost:3000/stylesheets/”就会看到一个404页面,将地址换成“http://localhost:3000/static/stylesheets/style.css”就可以了。

(3)路由级中间件

路由级中间件和应用级中间件类似,只是他绑定的对象express.Router(),还是直接抛demo吧

//引入express.Router()方法
var app = express();
var router = express.Router();

// 没有挂载路径的中间件,通过该路由的每个请求都会执行该中间件

router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
router.get('/user/:id', function (req, res, next) {
  // 如果 user id 为 0, 跳到下一个路由
  if (req.params.id == 0)  next('route');
  // 负责将控制权交给栈中下一个中间件
  else next(); //
}, function (req, res, next) {
  // 渲染常规页面
  res.render('OK');
});

// 处理 /user/:id, 渲染一个特殊页面
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

//将路由挂载到应用上
app.use('/',router)
2.2.4响应方法

下表中响应对象(res)的方法向客户端返回响应,终结请求响应的循环。如果在路由句柄中一个方法也不调用,来自客户端的请求会一直挂起。

image

2.4 功能开关,变量容器

app.set()app.get() 可以用来保存 app 级别的变量(对, app.get() 还和 GET 方法的实现名字上还冲突了):

const express = require('express');

const app = express();

app.all('/', (req, res) => {
    app.set('title', '标题123');
    res.send('ok');
});

app.all('/t', (req, res) => {
    res.send(app.get('title'));
});

app.listen(8888);

上面的代码,启动之后直接访问 /t 是没有内容的,先访问 / 再访问 /t 才可以看到内容。

对于变量名, Express 预置了一些,这些变量的值,可以叫 settings ,它们同时也影响整个应用的行为:

  • case sensitive routing
  • env
  • etag
  • jsonp callback name
  • json escape
  • json replacer
  • json spaces
  • query parser
  • strict routing
  • subdomain offset
  • trust proxy
  • views
  • view cache
  • view engine
  • x-powered-by

具体的作用,可以参考 https://expressjs.com/en/4x/api.html#app.set

(上面这些值中,干嘛不放一个最基本的 debug 呢……)

除了基本的 set() / get() ,还有一组 enable() / disable() / enabled() / disabled() 的包装方法,其实就是 set(name, false) 这种。 set(name) 这种只传一个参数,也可以获取到值,等于 get(name)

app.set()设定表可查看
express 4.x版本官网的app.set()的属性和使用方法

2.5 视图与模板引擎

在开始之前,我们有必要解释下何为* 视图引擎(view engine)*?视图引擎作为编程术语它主要意思是指“进行视图渲染的模块”。而 Express 框架中最常用的两个视图引擎是 Pug 和 EJS 。需要注意的是,Pug 早期的名称是 Jade 由于某些原因不得已改名 。
另外,Express 并没有指定必须使用何种引擎。只要该视图引擎的设计符合 Express API 规范,你就可以将其应用到工程中。下面,我们看看它到底是如何工作的。

下面我们通过一个简单示例回顾下 EJS 渲染过程:

var express = require("express");
var path = require("path");
var app = express();

app.set("view engine", "ejs");
app.set("views", path.resolve(__dirname, "views"));
app.get("/", function(req, res) {
    res.render("index",{name:res.params.name});
});

app.listen(3000);复制代码在运行代码之前,你需要通过 npm install 安装 EJS 和 Express。在安装完成后访问应用主页的话,程序就会寻找 views/index.ejs 文件并使用 EJS 对其进行渲染。另外,工程中一般都只会使用一个视图引擎,因为多个引擎会给工程引入不必要的复杂性。

express用esj---改为文件是html后缀的:

var express = require("express");
var app = express();

// 规定何种文件用何种方法来渲染
app.engine('html', require('ejs').__express);
//设置模板引擎中模板所在的文件夹
app.set('views', process.cwd() + '/views');
//设置视图引擎的后缀名,当render函数的路径省略了后缀的时候,用这个来适配默认的后缀以查找引擎。
 app.set('view engine', 'ejs');
app.get('/views/:name', function(req, res) {
     res.render('index', {
         appName: req.params.name,
         appTitle: 'peng'
     });
 });
app.listen(3000);

EJS 语法

除了用做 HTML 模版之外,它还能应用于字符串和纯文本中。请看 EJS 是如何对下面文本模版进行渲染的:

Hi <%= name %>!
You were born in <%= birthyear %>, so that means you're <%= (new Date()).getFullYear() - birthyear %> years old.
<% if (career) { -%>
  <%=: career | capitalize %> is a cool career!
<% } else { -%>
  Haven't started a career yet? That's cool.
<% } -%>

将下面的 JSON 数据传入上面摸板中:

{
    name: "Tony Hawk",
    birthyear: 1968,
    career: "skateboarding",
    bio: "<b>Tony Hawk</b> is the coolest skateboarder around."
}

最终渲染出来的:

Hi Tony Hawk!
You were born in 1968, so that means you’re 47 years old.
Skateboarding is a cool career!
Oh, let’s read your bio: Tony Hawk is the coolest skateboarder around. See

上面代码该示例演示了 EJS 常用的四种语法:打印打印并转义执行 JS 代码过滤
在 EJS 你可以使用两种语法打印表达式的值:<%= expression %><%- expression %>,其中前者会对结果进行 HTML 转义。例如,当传入的 expression 值为 Express 时,前者执行的结果是 Express 而后者得到的字符串是 Express。我建议你使用前一种方式,因为它更为可靠。
同样,EJS 还允许你 通过 * <% expression %>* 语法在其中执行 JS 表达式,并且该表达式并不会被打印出来。该特性在执行循环和条件判断的时候非常有用。另外,你还可以通过 * <% expression -%>* 避免不必要的换行。
通过 * <%=: expression | xxx %>* 语法,我们可以对表达式结果再进行一次过滤处理。例如,上面我们就对表达式结果应用了首字母大写过滤器。当然,除了自带的大量过滤器之外,你还可以进行自定义。

在已有 EJS 文件中嵌入其他 EJS 模版
EJS 引擎允许你在当前模版中使用另一个 EJS 模版。这样我们就能对整个进行组件拆分复用。例如,将 HTML 的头部和尾部拆分为 header 和 footer 模块,然后在其他模版中进行组合复用。

示例如下:首先我们创建 index.ejs 并拷贝代码:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>
        <%= appTitle %>
    </title>
</head>

<body>
    <%-include ('./header.ejs')%>
</body>

</html>

</html>
紧接着创建 footer 组件 footer.ejs 并拷贝代码:

<h1>
    <%= appName %>
</h1>

2.6 请求和响应

2.6.1 请求 Request

这一块倒没有太多可以说的,一个请求你想知道的信息,都被包装到 req 的属性中的。除了,头。头的信息,需要使用 req.get(name) 来获取。

GET 参数

使用 req.query 可以获取 GET 参数:

const express = require('express');
const app = express();

app.all('/', (req, res) => {
    console.log(req.query);
    res.send('ok');
});

app.listen(8888);

请求:

# -*- coding: utf-8 -*-
import requests
requests.get('http://localhost:8888', params={"a": '中文'.encode('utf8')})

POST 参数

POST 参数的获取,使用 req.body ,但是,在此之前,需要专门挂一个 Middleware , req.body才有值:

const express = require('express');
const app = express();

app.use(express.urlencoded({ extended: true }));
app.all('/', (req, res) => {
    console.log(req.body);
    res.send('ok');
});

app.listen(8888);

# -*- coding: utf-8 -*-

import requests

requests.post('http://localhost:8888', data={"a": '中文'})

如果你是整块扔的 json 的话:

# -*- coding: utf-8 -*-

import requests
import json

requests.post('http://localhost:8888', data=json.dumps({"a": '中文'}),
              headers={'Content-Type': 'application/json'})

Express 中也有对应的 express.json() 来处理:

const express = require('express');
const app = express();

app.use(express.json());
app.all('/', (req, res) => {
    console.log(req.body);
    res.send('ok');
});

app.listen(8888);

Express 中处理 body 部分的逻辑,是单独放在 body-parser 这个 npm 模块中的。 Express 也没有提供方法,方便地获取原始 raw 的内容。另外,对于 POST 提交的编码数据, Express 只支持 UTF-8 编码。

如果你要处理文件上传,嗯, Express 没有现成的 Middleware ,额外的实现在 https://github.com/expressjs/multer 。( Node.js 天然没有“字节”类型,所以在字节级别的处理上,就会感觉很不顺啊)

Cookie

Cookie 的获取,也跟 POST 参数一样,需要外挂一个 cookie-parser 模块才行:

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser())
app.all('/', (req, res) => {
    console.log(req.cookies);
    res.send('ok');
});

app.listen(8888);

请求:

# -*- coding: utf-8 -*-

import requests
import json

requests.post('http://localhost:8888', data={'a': '中文'},
              headers={'Cookie': 'a=1'})
2.6.2响应 Response

Express 的响应,针对不同类型,本身就提供了几种包装了。

普通响应
使用 res.send 处理确定性的内容响应:

res.send({ some: 'json' });
res.send('<p>some html</p>');
res.status(404); res.end();
res.status(500); res.end();

express请求和响应详情:
https://www.cnblogs.com/rope/p/10726134.html

3. Express实践

3.1使用express做一个后端接口返回数据

打开刚才我们创建的项目,routes/users.js文件。可以看到:

/ routes/users.js文件
 
var express = require('express');
var router = express.Router();
var data = {
    'code':'000',
    'message':'message消息',
    'lists':[
        {
          'naem':'小马',
            'age': '12',
            'sex':'男'
        },{
          'naem':'小韦',
            'age': '12',
            'sex':'女'
        }
    ]
 
}
 
/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send(data);
});
 
module.exports = router;

改了之后再请求没有改变,查看命令行信息,304发现我们的请求给我们缓存了;

GET /users 304 2.543 ms - -

重新启动一下试试,刷新就可以了

{
  'code':'000',
  'message':'message消息',
  'lists':[
    {
      'naem':'小马',
      'age': '12',
      'sex':'男'
    },{
      'naem':'小韦',
      'age': '12',
      'sex':'女'
    }
  ]
}

这样虽然可以请求,但是有缓存问题,而且接口多的话每次都得配置一个路径,太复杂,所以要封装一下,重复使用,每次请求重新请求不加载缓存。

在项目下新建一个config文件夹并新建一个api.js,配置一下:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

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

var app = express();
// 引入API
var api = require('./config/api');

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

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

/*配置请求*/
app.get('/', function(req, res) {
    res.send('hello world');
});
app.get('/api/*', api.get);
app.post('/api/*', api.post);

app.options('/api/*', function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
    res.sendStatus(200); /*让options请求快速返回*/
});

module.exports = app;

根目录新建api文件夹,新建demo.js;

// api/demo.js
 
var dataDemo = {
    'code': '0',
    'message': 'message消息',
    'lists': [
        {
            'naem': '小马',
            'age': '12',
            'sex': '男'
        }, {
            'naem': '小韦',
            'age': '12',
            'sex': '女'
        }
    ]
}
exports.getData = function (method, data) {
    var backData = {
        "code": '0',
        "msg": "demo1",
        "data": dataDemo
    }
    if (method == 'DELETE') {
        backData = {
            "code": '999',
            "msg": "不支持DELETE方法"
        }
    }
    return JSON.stringify(backData);
}

重新运行npm start 之后,浏览器打开localhost:3000,此时应该显示的是hello world;

打开localhost:3000/api/demo,页面显示返回的json.
到此都是成功的,那就说明配置成功了,在api文件夹下复制一个js文件命名为demo2.js,改动一下数据,浏览器访问localhost:3000/api/demo2就可以访问不同的数据了;
开发的时候指向本地服务器器接口,联调测试上线的时候只需要把指向本地服务器地址替换成线上地址一下就可以了;

var baseUrl = 'https://www.huizhi.com';   // 线上地址
var baseUrl = 'http://localhost:3000/api';  // 本地服务器地址

参考文章:
根据源码模拟实现express框架常用功能
前端模拟后端接口返回数据的方法
NodeJS插件Express里面的engine 和view engine的关系
Express&ejs模板引擎&请求识别
nodejs开发 过程中express路由与中间件的理解
Express 实战(七):视图与模板:Pug 和 EJS
webpack4 + ejs + express 带你撸一个多页应用项目架构
《一起学 Node.js》彻底重写完毕

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,738评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,377评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,774评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,032评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,015评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,239评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,724评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,374评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,410评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,457评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,132评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,733评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,804评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,022评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,515评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,116评论 2 341

推荐阅读更多精彩内容