课程地址

先安装express

新建一个文件夹如nodeApi,进入,然后npm init,会生成package.json
之后npm i express安装完express
新建index.js

最简单的监听服务器

const express = require('express');
const app = express();
//以上两行为标准动作

app.get('/', (req, res) => {
  res.send('hello world');
  //res.send([1,2,3]);返回数组
});
//req是指request, res是指response
//话说后面的function(req, res)好像不用写function了,是直接把function省略掉了吗?

app.listen(3000, ()=>console.log('listening on port 3000...'));
//此处好像也是把function省略掉了

之后终端输入node index.js
之后浏览器localhost:3000可以看到hello world

使用nodemon持续监听

如果用node index.js的话,每次修改文件都需要ctrl+c取消重新启动
安装nodemon(node monitoring)就可以不用手工重启

修改端口避免冲突

const port = process.env.PORT || 3000; //使用环境端口或者3000。我有点好奇,如果3000被占用了
app.listen(port, ()=>console.log(`listening to port ${port}...`));
//监听port,后面为啥用``和${port}就可以插入变量?第一次看见这个
//不过我使用set PORT=5000之后,listen还是在3000,不知道为什么

获取路径中的基础参数

app.get('/api/courses/:id', (req, res)=>{
  res.send(req.params.id);
});
//可以使用req.params.id来获取id这个参数,
//可以有多个参数,比如/api/courses/:id/:name/:age

获取问号后的查询参数

app.get('/api/courses/:id', (req, res)=>{
  res.send(req.query);
});
//在这里的路径里并不需要定义,query就可以自动把问号后的记录下来

通过参数查询数据

假设有个课程列表

const courses = [
  { id: 1, name: 'course1'},
  { id: 2, name: 'course2'},
  { id: 3, name: 'course3'}
];

app.get('/api/courses/:id', (req, res)=>{
  const course = courses.find(c => c.id === parseInt(req.params.id));
  //使用courses.find方法,通过c.id等于传入的id来查询,parseInt是把文本转成数字的方法
  //至于c => c.id是什么操作,我还是不太明白这个箭头函数
  if (!course) res.status(404).send('the course is not found'); //返回404状态码,并send错误信息
  res.send(course) //如果course不为空,返回course
  //这个if () true第二行false的写法,也是第一次见到...
});

此时访问localhost:3000/api/courses/1,会获得返回的course,是一个json格式的

{id:3, name:'course1'}

如果访问localhost:3000/api/courses/4,会获得返回的错误信息

使用post方法新增数据

app.use(express.json());
//需要使用这个组件,要不然的话不能使用courses.push操作json数据(应该是这样的吧...)

app.post('/api/courses', (req, res) => {
  const course = {
    id: courses.length + 1,
    name: req.body.name  //注意post的内容是通过req.body来获得的
  };
  courses.push(course); //将新增的course添加到courses里
  res.send(course);  //返回新增的数据
});

这里需要使用postman来进行验证测试
安装postman,选择post,URL:http://localhost:3000/api/courses
选择body, 选择raw,选择JSON(application/json)
然后输入:

{
  "name":"new course"
}

点击“发送”,在console里会收到返回的数据

{
  "id": 4,
  "name": "new course"
}

添加验证

如果post过来的参数不符合要求,就需要做验证

app.post('/api/courses', (req, res) => {
  if (!req.body.name || req.body.name.length < 3) {  //如果name是空的,或者长度小于3
    res.status(400).send('name can\'t be null and minimun 3 characters');
    return;  //这里用return来阻止后面代码的运行
  } 

  const course = {
    id: courses.length + 1,
    name: req.body.name  //注意post的内容是通过req.body来获得的
  };
  courses.push(course); //将新增的course添加到courses里
  res.send(course);  //返回新增的数据
});

使用post可以测试验证是否正常

使用Joi验证组件

因为每一次都写各种验证器太繁琐,于是有了Joi这个验证管理组件(我觉得是这样)

使用npm i joi@13.1.0安装视频中的版本
在最上面引入,const Joi = require('joi');

创建一个schema,里面包含了每个字段的格式要求

const schema = {
  name: Joi.string().min(3).required  //string格式、最小3字符、必填
};

接着使用validate

const result = Joi.validate(req.body, schema); //使用schema的规则去验证req.body
console.log(result); //可以查看验证结果

如果验证成功,result.error就是null,可以用这个来判断进行下一步操作
修改之前的代码:

  if (!req.body.name || req.body.name.length < 3) {  //如果name是空的,或者长度小于3
    res.status(400).send('name can\'t be null and minimun 3 characters');
    return;  //这里用return来阻止后面代码的运行
  } 

改成:

  if (result.error) {  //如果error不为空,表示验证失败,返回失败信息
    res.status(400).send(result.error.details[0].message);
    return;  //这里用return来阻止后面代码的运行
  } 

使用put更新信息

app.put('/api/courses/:id', (req, res) => {
  //根据id查看是否能找到课程
  const course = courses.find(c => c.id === parseInt(req.body.id));
  if (!course) res.send('can not find the course by id');

  //验证参数是否符合规则
  const schema = {
    "name": Joi.string().min(3).required()
  };  
  const result = Joi.validate(req.body, schema);
  if (result.error) {
    res.status(400).send(result.error.details[0].message);
    return;
  }

  //更新参数值
  course.name = req.body.name;

  //返回更新后的数据
  res.send(course);
});

将validate方法独立出来

function validateCourse(course){
  const schema = {
    "name": Joi.string().min(3).required()
  };  
  return result = Joi.validate(course, schema);
}

然后在前面需要使用的地方调用此方法

const result = validateCourse(req.body);

这里有个方法,如果我们关注的是result里的error,一般我们会使用result.error来获取,但有个更加简洁的办法,就是直接使用:

const { error } = validateCourse(req.body);

就可以直接获取error里面的信息了。

把之前post方法里的验证部分,也改成这样,只需要copy过去就可以了。

**使用delete方法

app.delete('/api/courses/:id', (req, res) => {
    //查找是否有此课程
    const course = courses.find(c => c.id === parseInt(req.params.id));
    if (!course) res.status(404).send('The course with given ID was not found');

    //删除此课程
    //需要获取通过id找到的course的index,然后通过index并使用splice方法来删除课程
    const index = courses.indexOf(course);
    courses.splice(index, 1);

    //返回删除成功
    res.send(course);
});

简洁代码判断查找成功与否

判断是否能找到课程,之前是这么写的:

//查看是否能找到
const course = courses.find(c => c.id === parseInt(req.params.id));
if (!course) res.status(404).send('The course with given ID was not found');
return;

更好的写法是:

if (!course) return res.status(404).send('The course with given ID was not found');
//如果没有找到course,就返回404和错误信息,如果找到了,就执行后面的代码,一行就表达清楚了

验证内容是否符合要求,之前是这么写的:

//验证内容
const { error } = validateCourse(req.body);
if (error){
    //400 bad request
    res.status(400).send(error.details[0].message);
    return;
}

同样可以应用上面的代码,使其更加简洁:

//验证内容
const { error } = validateCourse(req.body);
if (error) return res.status(400).send(error.details[0].message);
....

完整代码

const Joi = require('joi');
const express = require('express');
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 given ID was not found');
    res.send(course);
});

app.post('/api/courses', (req, res) => {
    //验证内容
    const { error } = validateCourse(req.body);
    if (error) 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) => {
    //查看是否能找到
    const course = courses.find(c => c.id === parseInt(req.params.id));
    if (!course) return res.status(404).send('The course with given ID was not found');

    //验证内容
    const { error } = validateCourse(req.body);
    if (error) return res.status(400).send(error.details[0].message);

    //更新name //为什么course是const又能改呢?
    course.name = req.body.name;
    
    //返回更新的信息
    res.send(course);
});

app.delete('/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 given ID was not found');

    //删除此课程
    const index = courses.indexOf(course);
    courses.splice(index, 1);

    //返回删除成功
    res.send(course);
});

function validateCourse(course){
    const schema = {
        name: Joi.string().min(3).required()
    };
    return Joi.validate(course, schema);
};

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

课程总结

这个课程学习到了对数据的增删改查,我觉得还是很简单易懂的,老外的教程的确很不错。
我现在要克服一个MVC的思维,总觉得一定要在一个程序里面写MVC,但其实这种前后端分离的写法更加科学,前端我只需要用vue.js来调取api对数据进行增删改查就可以了,后端只需要响应前端的请求就可以了,没有必要一定要像thinkphp那样,把所有mvc都写在一套程序里。
另外一个缺点就是这个教程没有对数据库操作的部分,只好到另一个教程上去学了。

最后编辑:2021年01月05日 ©著作权归作者所有

发表评论