Nodejs基础1

Nodejs基础1

react基础2 react基础3表单与后端 react基础4授权与部署

这三篇博客中,我们讨论了vidly(一个电影展示项目)的客户端,但是我们在博客中仅仅调用了后端的api,对后端如何构建并没有提及。而Nodejs构造的后端是期末大作业中必不可少的一部分,所以这个系列让我们来谈谈如何用Nodejs+express+mongodb来构建一个vidly后端。

Building RESTful API’s Using Express

Introduction

下面利用express框架搭建后端

RESTful Services

大多数的网站结构如下:

RESTful 就是 Representational State Transfer 的缩写,是用来创建这些规范的HTTP服务的。我们利用简单的Http语言来进行增删改查,也就是CRUD operations

大多数的网站url如下

我们常常在.com后看到/api,这不是必要的规定,但是可以见到很多公司都遵循这个范式。customer代表了应用中用户的集合。在RESTful中我们把它看成是一种资源。我们可以把诸如用户,电影,租金或者各种资源都开放出去。所以可以是/rentals,/movies 不同的后缀代表操纵不同的用户资源。这个/customer可以对用户资源进行操作比如说创建用户,更新用户信息。

所有的HTTP请求都有所谓的动作或者方法,取决于他们的类型和目的

每个动作的模型如下

GET:获取到所有的用户的信息,返回一个列表。如果想单独得到一个用户的信息,需要这么写

/api/customers/1,这样就只返回一个用户的信息

PUT:用户更新

DELETE:删除操作

POST:新建操作

接下来我们把关注点集中在创建http服务上面。因此不适用数据库,就使用简单的数组在内存中保存数据

Introducing Express

express官方文档

express可以更方便的帮助我们管理路由

Express框架可以在良好维护性的前提下创建很多路由规则

新建一个express-demo文件夹,npm init —yes 新建package-json

npm install express

Building Your First Web Server

这里说一嘴 require和import的区别

import和require都是被模块化使用

  1. a. require是CommonJs的语法(AMD规范引入方式),CommonJs的模块是对象。
    b. import是es6的一个语法标准(浏览器不支持,本质是使用node中的babel将es6转码为es5再执行,import会被转码为require),es6模块不是对象

  2. a. require是运行时加载整个模块(即模块中所有方法),生成一个对象,再从对象上读取它的方法(只有运行时才能得到这 个对象,不能在编译时做到静态化),理论上可以用在代码的任何地方
    b. import是编译时调用,确定模块的依赖关系,输入变量(es6模块不是对象,而是通过export命令指定输出代码,再通过 import输入,只加载import中导的方法,其他方法不加载),import具有提升效果,会提升到模块的头部(编译时执行)
    export和import可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错
    es6这样的设计可以提高编译器效率,但没法实现运行时加载

  3. a. require是赋值过程,把require的结果(对象,数字,函数等),默认是export的一个对象,赋给某个变量(复制或浅拷贝)
    b. import是解构过程(需要谁,加载谁)

引用于此

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//我们首先引入express
const express = require("express");
//然后为了方便我们保存在app中,这个app就代表了我们的应用
const app = express();
/*
1.我们现在只用app.get() 这需要两个参数,第一个参数是路径,也就是URL;第二个参数是一个回调函数
2.回调函数在给定端口使用get方法时被调用。回调函数有两个函数,request和response,用req,res代替
3.然后是函数体的代码块。request参数给了我们很多请求的属性,可以参考exprss文档
4.我们这里申请了两个路由,一个返回helloworld,一个返回一个数组
5.最后我们要监听特定的端口,我们调用app.listen方法,给他一个端口号,然后给他一个回调函数,来显示
成功以后显示的文字
*/

app.get("/", (req, res) => {
res.send("Hello World");
});
app.get("/api/courses", (req, res) => {
res.send([1, 2, 3]);
});
app.listen(3000, () => console.log("Listening on port 3000.."));

Nodemon

Nodemon可以实时监控后端更改的内容并更新服务器。所以就不用每次修改以后重启服务器了

npm install -g nodemon

nodemon index.js 让nodemon检测该文件夹中所有文件、任何文件名和任何扩展名的改动

如果报错看这里

Environment Variables

我们现在是规定了在3000端口,但是可能会产生端口冲突,为了解决这个问题我们可以使用环境变量

1
2
3
4
5
6
7
8
9
10
11
12
const express = require("express");
const app = express();

app.get("/", (req, res) => {
res.send("Hello World!!!");
});
app.get("/api/courses", (req, res) => {
res.send([1, 2, 3]);
});
//port
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}..`));

然后设置 set PORT=5000,那么就有两种选择了

Route Parameters

我们现在想获得单一课程的信息也就是 /api/courses/1

我们在 react基础2 的路由部分有所提及 路由变量

1
2
3
app.get("/api/courses/:id", (req, res) => {
res.send(req.params.id);
});

也可以有多个变量

1
2
3
app.get("/api/posts/:year/:month", (req, res) => {
res.send(req.params );
});

当我们传入这个的时候,http://localhost:3000/api/posts/2020/6 会显示如下对象

使用这种表达式也可以读取查询字符串。也就是在url后面的参数

http://localhost:3000/api/posts/2020/6?sortBy=name

这个表达式的意思获取所有2020年6 月的帖子,然后根据名字来排序。sortBy=name 是查询字符串。我们使用它来向后端服务传递额外的参数。

所以我们用路由参数提供路由必须的数据与值,使用查询字符串传递附加的内容。

后端可以这么来读取查询字符串

1
2
3
app.get("/api/posts/:year/:month", (req, res) => {
res.send(req.query);
});

Handling HTTP GET Requests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//...
//首先定义一个数组
const courses = [
{ id: 1, name: "course1" },
{ id: 2, name: "course2" },
{ id: 3, name: "course3" },
];
//如果没有路由参数,那么把所有的都返回
app.get("/api/courses", (req, res) => {
res.send(courses);
});
//如果有参数,那么在数组中选择数组元素对象中id等于路由参数id的,返回(注意,req.params.id需要转换)
app.get("/api/courses/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)//那么如果这个id不存在的话,我们就需要返回404
res.status(404).send("The course with the given ID was not found ");
res.send(course);
});

入读,我们id=1,匹配到,那么就返回这个对象。否则返回 The course with the given ID was not found

Handling HTTP POST Requests

现在我们利用post请求创建新的课程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
为了能让这个put正常工作,我们需要打开Express获取请求体中JSON对象的功能,我们需要手动设置
这里我们做的其实是添加了一个中间件。当我们调用express.json的时候这个方法返回了一个中间件
然后我们用use方法在处理请求流程中使用这个中间件
*/
app.use(express.json());
//...
app.post("/api/courses", (req, res) => {
/*
在handler中我们需要新建一个课程信息,然后把他添加到课程数组当中去
因为没有数据库所以我们要手动设置ID,之后用了数据库ID就会由数据库自动生成
这里假设在request中有一个对象,对象中包含了一个name属性。
*/
const course = {
id: courses.length + 1,
name: req.body.name,
};
courses.push(course);//利用push把这个对象加入到courses数组中
//最后作为惯例,当我们让服务器创建了新的对象或者资源的时候,我们应返回
//因为客户端可能需要用到这个新创建的值
res.send(course);
});

Calling Endpoints Using Postman

我们利用google插件postman,在react基础3表单与后端 中有所介绍

这就是如何使用postman测试http终端。在实现过程中我们假设请求体中包含一个name属性的对象。

Input Validation

但是如果用户传入了不合法的数据,那么我们需要对其验证。

从安全角度,我们永远不要相信客户输入的内容,所以我们要永远验证输入的内容

在这里我们可以简单写一个验证逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
app.post("/api/courses", (req, res) => {
if (!req.body.name || req.body.name.length < 3) {
//400 Bad Request
res.status(400).send("Name is required and should be minimum 3 characters");
return;
}
const course = {
id: courses.length + 1,
name: req.body.name,
};
courses.push(course);
res.send(course);
});

如果不想写复杂的逻辑,我们可以导入一个包 在react基础3表单与后端 中有提到Joi。在后端也可以使用

npm i joi@13.1.0

1
2
3
4
5
6
7
8
9
10
11
app.post("/api/courses", (req, res) => {
const schema = {
name: Joi.string().min(3).required(),
};
const result = Joi.validate(req.body, schema);
if (result.error) {
//400 Bad Request
console.log(result);
res.status(400).send(result.error.details[0].message);
return;
}

如果正确,那么我们就打印出来的是一个对象,对象中error属性为null

反之我们对象中就有一个错误了

我们如果res.status(400).send(result.error);会显示一个比较复杂的对象,我们整整需要的是details中的message

所以我们改成这样res.status(400).send(result.error.details[0].message);当再次发送空的name或者name.length<3 的话,在postman中我们得到这两个报错

“name” is required

“name” length must be at least 3 characters long

Handling HTTP PUT Requests

这里重构了一下,把验证单独抽离出来成为一个函数。

逻辑是这样的,先验证是否存在这个id,再验证要修改的内容是否合法,最后进行更新和返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function validateCourse(course) {
const schema = {
name: Joi.string().min(3).required(),
};
return Joi.validate(course, schema);
}
app.put("/api/courses/:id", (req, res) => {
// Look up the course
//If not existing ,return 404
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
res.status(404).send("The course with the given ID was not found ");
//Validate
//If invalid,return 400-Bad request
const { error } = validateCourse(req.body);
if (error) {
//400 Bad Request
res.status(400).send(error.details[0].message);
return;
}
//Update course
course.name = req.body.name;
//Return the updated course
res.send(course);
});

Handling HTTP Delete Requests

删除的逻辑:首先找到movie.id,找不到返回404,找到了我们就取这个课程的id。通过splice函数删除

splice(从第几个元素开始,删除几个元素)

1
2
3
4
5
6
7
8
9
10
11
12
13
app.delete("/api/courses/:id", (req, res) => {
//look up the course
//Not existing ,return 404
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
res.status(404).send("The course with the given ID was not found ");
//Delete
const index = courses.indexOf(course);
courses.splice(index, 1);

// Return the same course
res.send(course);
});

如果传入http://localhost:3000/api/courses/10

得到 The course with the given ID was not found

我们先来删除,再获取,发现已经找不到courses/1了

代码还需要做一个小改进,就是当发现错误的时候,立即return

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const express = require("express");
const Joi = require("joi");
const app = express();
app.use(express.json());
const courses = [
{ id: 1, name: "course1" },
{ id: 2, name: "course2" },
{ id: 3, name: "course3" },
];

app.get("/", (req, res) => {
res.send("Hello World!!!");
});
app.get("/api/courses", (req, res) => {
res.send(courses);
});

app.get("/api/courses/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
return res.status(404).send("The course with the given ID was not found ");
res.send(course);
});
app.get("/api/posts/:year/:month", (req, res) => {
res.send(req.query);
});
function validateCourse(course) {
const schema = {
name: Joi.string().min(3).required(),
};
return Joi.validate(course, schema);
}

app.post("/api/courses", (req, res) => {
const { error } = validateCourse(req.body);
if (error)
//400 Bad Request
return res.status(400).send(error.details[0].message);
const course = {
id: courses.length + 1,
name: req.body.name,
};
courses.push(course);
res.send(course);
});

app.put("/api/courses/:id", (req, res) => {
// Look up the course
//If not existing ,return 404
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
return res.status(404).send("The course with the given ID was not found ");
//Validate
//If invalid,return 400-Bad request
const { error } = validateCourse(req.body);
if (error)
//400 Bad Request
return res.status(400).send(error.details[0].message);

//Update course
course.name = req.body.name;
//Return the updated course
res.send(course);
});

app.delete("/api/courses/:id", (req, res) => {
//look up the course
//Not existing ,return 404
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
return res.status(404).send("The course with the given ID was not found ");
//Delete
const index = courses.indexOf(course);
courses.splice(index, 1);
res.send(course);
// Return the same course
});
//port
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}..`));

Project- Build the Genres API

现在我们来船舰vidly应用的后端

首先我们创建一个管理电影分类的终端。每个电影都有自己的分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
const Joi = require('joi');
const express = require('express');
const app = express();

app.use(express.json());

const genres = [
{ id: 1, name: 'Action' },
{ id: 2, name: 'Horror' },
{ id: 3, name: 'Romance' },
];

app.get('/api/genres', (req, res) => {
res.send(genres);
});

app.post('/api/genres', (req, res) => {
const { error } = validateGenre(req.body);
if (error) return res.status(400).send(error.details[0].message);

const genre = {
id: genres.length + 1,
name: req.body.name
};
genres.push(genre);
res.send(genre);
});

app.put('/api/genres/:id', (req, res) => {
const genre = genres.find(c => c.id === parseInt(req.params.id));
if (!genre) return res.status(404).send('The genre with the given ID was not found.');

const { error } = validateGenre(req.body);
if (error) return res.status(400).send(error.details[0].message);

genre.name = req.body.name;
res.send(genre);
});

app.delete('/api/genres/:id', (req, res) => {
const genre = genres.find(c => c.id === parseInt(req.params.id));
if (!genre) return res.status(404).send('The genre with the given ID was not found.');

const index = genres.indexOf(genre);
genres.splice(index, 1);

res.send(genre);
});

app.get('/api/genres/:id', (req, res) => {
const genre = genres.find(c => c.id === parseInt(req.params.id));
if (!genre) return res.status(404).send('The genre with the given ID was not found.');
res.send(genre);
});

function validateGenre(genre) {
const schema = {
name: Joi.string().min(3).required()
};

return Joi.validate(genre, schema);
}

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));

A Quick Note

Express- Advanced Topics

Introduction

在这章节我们会学Middleware,Configuration,Debugging,Template Engines

Middleware

express中的一个核心概念就是中间件(中间件函数) 。一个中间件技术上说就是:我们得到一个请求对象,要么返回客户端,要么传递给一个中间件。

在Express当中,所有route handler都是中间件:因为它需要传入一个请求对象,并且在这里向客户端返回数据。 express.json() 这个函数返回一个中间件。这个函数的作用就是读取请求,如果请求体是一个JSON格式的对象。他就会格式化这个JSON对象并以此设置req.body属性

处理模型大致是这样的。当服务器收到一个请求,请求就进入一个管道,我们将这个管道成为请求处理管道(Request Processing Pipeline) 管道之中由一个或者多个中间件。每个中间件要么根据请求向客户端返回数据,要么将控制权交给其他的中间件

之前的代码中这个 RPP中由两个中间件,一个是把请求转化成一个JSON格式对象,它并没有终结Request Response Circle,所以他把控制权交给了下一个中间件,也就是route handler 。route handler中 req.body属性已经设置好了,这样就可以进行一些操作了。 最后向客户端发送反馈来终结 Request Response Circle

Express中有内部中间件函数,我们同样也可以在RPP中添加自定义的中间件函数

使用自定义的中间件,我们可以创建 Crosscutting Concerns,比如实现登录验证等功能。

Creating Custom Middleware

我们通过调用use方法在RPP中安插一个中间件。传入一个函数,里面有三个参数。next表示管道中下一个中间件的引用。这里写了两个中间件。注意,一定要写next()把控制权交给下一个中间件,否则的话我们没有闭合 Request Response Circle,那么就一直显示Loading…

中间件是按照顺序调用的,首先调用logging函数,其次调用 Authenticating函数

1
2
3
4
5
6
7
8
app.use(function(req,res,next){
console.log('logging...');
next();
})
app.use(function(req,res,next){
console.log('Authenticating...');
next();
})

为了让代码变得更加简明,当我们创建中间件的时候我们不要写在index中。我们要把每个中间件放在各自独立的文件当中

新建一个logger.js存放我们的中间件

1
2
3
4
5
function log(req, res, next) {
console.log("logging...");
next();
}
module.exports = log;

在index中导入

1
2
3
const logger = require("./logger");
app.use(express.json());
app.use(logger);

现在理解了express.json()的意思了吧。当我们调用它时,他返回了一个需要三个参数的函数。req,res,next

他格式化了请求体,如果是一个JSON对象他就设置为req.body属性

Built-in Middleware

Express有很多Built-in 中间件,比如 express.json()

还有类似的中间件app.use(express.urlencode());

app.use(express.static(’public‘)); 我们可以把所有静态文件(css,图片等)放到这里去,static中间件是从根目录开始起作用的

这样就可以通过url访问这些文件了

Third-party Middleware 第三方中间件

官方文档中有很多中间件

这里我们使用helmet 中间件,Helps secure your apps by setting various HTTP headers

npm i helmet

1
2
const helmet = require("helmet");
app.use(helmet);

另一个有用的中间件是Morgan,我们利用它来进行HTTP请求的日志记录.这里是用最简单的格式,tiny

1
2
const morgan = require("morgan");
app.use(morgan("tiny"));

使用了Morgan以后,我们再进行操作的话,后端就会出现日志

顺便说,Morgan默认请求实在控制台记录日志。同样也可以设置它写在日志文件当中。我可以有一个配置文件,在某些特定的场景下可以短时间开启这个功能。(开启后对运行效率有影响)

Environments

也许我们想依照环境类型决定是否开关某功能。例如我们只想再开发环境当中开启对HTTP请求的日志记录

我们利用process对象,他是node的全局对象,通过它可以访问当前的进程,这个process对象有一个env属性

它提供我们环境变量的值。有一个标准的环境变量是NODE_ENV。 这个值返回当前node所在环境的值。如果他没有被设置,那么他就会返回未定义。同样我们可以在外部设置它,把它设置为production,testing等等

还有一种方法可以获得默认环境,是app的一个方法app.get(“env”),这其实默认调用了NODE_ENV,但是当NODE_ENV未定义的时候,这个方法默认返回开发环境的值

我们来打印一下默认的环境

1
2
console.log(`NODE_ENV:${process.env.NODE_ENV}`);
console.log(`app:${app.get("env")}`);

NODE_ENV:undefined
app:development
在这个例子当中我们只想在开发环境使用日志。我们就可以这么写代码

1
2
3
4
if(app.get('env')==='development'){
app.use(morgan('tiny'));
console.log('Morgan enabled')
}

但是我们如果把环境设为生产,Morgan就不会起作用了

在Windows通过set命令来设置环境变量

set NODE_ENV = production

Configuration

现在来讨论如何保存应用的配置数据。并且在不同的环境中复写对应配置

比如在测试环境,我可以需要使用一个不同的数据库或者邮件服务器。

我们可以利用 rc 包

https://www.npmjs.com/package/rc

也可以使用 config包

https://www.npmjs.com/package/config

npm i config

我们先在根目录下新建一个config文件夹

里面有三个JSON文件:default,development,production

1
2
3
{
"name":"My Express App"
}
1
2
3
4
5
6
{
"name": "My Express App =Development",
"mail": {
"host": "dev-mail-server"
}
}
1
2
3
4
5
6
{
"name": "My Express App =Development",
"mail": {
"host": "prod-mail-server"
}
}

然后再index.js中导入这个包。利用get方法就会自动根据当前的环境变量找到相应的文件

1
2
3
4
const config = require("config");
//configuration
console.log("Application Name:" + config.get("name"));
console.log("Maile Server Name:" + config.get("mail.host"));

输出:

Application Name:My Express App =Development
Maile Server Name:dev-mail-server

利用这个模块我们可以轻松的设置不同的环境的配置

但是不能把密码等机密放在json文件夹当中。因为这样很不安全,每个人都能看见。。

我们应该把他们保存在环境变量当中。我们来设置一下

终端中输入

1
set app_password=1234

然后我们新建一个 custom-environment-variables (名字一定要写对)

在这个文件只有映射关系,映射环境变量和应用配置的关系

在这里只有password 到app_password 的映射关系

1
2
3
4
5
{
"mail": {
"password":"app_password"
}
}

Debugging

如果用console.log()来调试,会很麻烦,改来改去。所以更好的在控制台调试的方式是使用node的debug模块

也就是说用debug函数替换所有的console.log 这样就不需要注释掉他们,可以通过在外部修改环境变量实现

1
2
const startupDebugger = require("debug")("app:startup");
const dbDebugger = require("debug")("app:db");

然后通过

1
set DEBUG=app:startup

查看Debug信息

Templating Engines

有时候我们需要返回HTML标记语言文本到客户端而不是JSON文件,那么我们就需要模板引擎了

那么我们新建一个views文件夹,然后在里面新建一个index.pug文件

1
2
3
4
5
html
head
title=title
body
h1=message

在index.js中配置

1
2
app.set("view engine", "pug");
app.set("views", "./views"); //optional

虽有这样修改,可以看到Helloworld已经被渲染出来了

但是我们后端不需要什么视图引擎或者模板引擎

Database Integration

Authentication

Express没有验证功能

Structuring Express Applications

在现实开发中我们肯定不会把所有的东西放到index.js当中去。所以我们要正确的结构化我的应用

第一件事就是清理所有设计courses的接口,并且把它放到一个独立的文件当中去。换句话说,每个独立的api终端的逻辑代码,都要转为一个独立的文件或者模块。所有的coureses的路由都要丢到一个courses.js文件当中去

我们在根目录新建一个新的routes文件夹里面有文件courses.js。把所有包含courses.js的文件全部放到courses.js中

courses.js的配置 把app都换成router

1
2
3
4
const express = require("express");
const router = express.Router();
//...
module.exports = router;

同样的,把home.js 抽离出来

1
2
3
4
5
6
7
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.render("index", { title: "My Express App", message: "Helloworld" });
});

module.exports = router;

最后,把logger放到middleware文件夹当中

index.js中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const startupDebugger = require("debug")("app:startup");
const dbDebugger = require("debug")("app:db");
const config = require("config");
const express = require("express");
const app = express();
const logger = require("./middleware/logger");
const helmet = require("helmet");
const morgan = require("morgan");
const courses = require("./routes/courses");
const home = require("./routes/home");
app.set("view engine", "pug");
app.set("views", "./views"); //optional
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(logger);
app.use(helmet());
// route全部以/api/courses开头,那么courses.js中就可以把所有的/api/courses都去掉了,只留下:id。
app.use("/api/courses", courses);
app.use("/", home);
//configuration
console.log("Application Name:" + config.get("name"));
console.log("Mail Server Name:" + config.get("mail.host"));
// console.log("Maile Server Password:" + config.get("mail.password"));
if (app.get("env") === "development") {
app.use(morgan("tiny"));
startupDebugger("Morgan enabled");
}
//Db work
dbDebugger("Connected to the database");

//port
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}..`));

Project- Restructure the App

现在我们重构之前的vidly项目,

index.js

1
2
3
4
5
6
7
8
9
const Joi = require("joi");
const express = require("express");
const app = express();
const genres = require("./routes/genres");
app.use(express.json());

app.use("/api/genres", genres);
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));

genres.js 替换app为route, 替换/api/genres为/

1
2
3
4
5
const Joi = require("joi");
const express = require("express");
const router = express.Router();
//...
module.exports = router;
-------------本文结束,感谢您的阅读-------------