RESTful(Express)

RESTful

Introducing Express

Express是轻量级、快速、且拥有良好文档系统的Node框架,访问www.npmjs.com搜索express获取更多信息。

安装express

npm i express

Building Your First Web Server

新建一个index.js 或 app.js。加载express模块,它返回一个函数,将函数储存为express变量。这个函数则返回一个类型为Express的对象,储存为变量app。

const express = require("express"); // Return a function
const app = express(); // Return an object with type of Express

首先看 app.get(),接收两个参数

  • URL
  • 回调函数,当对给定端口使用get方法时候调用

访问文档以获取更多request、response的属性

app.get("/", (req, res) => {
  res.send("Hello World");
});
// Visit document to get more properties of req and res.
app.listen(3000, () => console.log('Listening on the port 3000...'))

现在在控制台运行 node index.js,访问 localhost:3000,就能看到Hello World.

再写一个。

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

再次运行 node index.js,访问 localhost:3000/api/courses,就能看到对应的数组.

// index.js
const express = require("express"); // Return a function
const app = express(); // Return an object with type of Express

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

app.get("/api/courses", (req, res) => {
  res.send([1, 2, 3]);
});
// Visit document to get more properties of req and res.
app.listen(3000, () => console.log('Listening on the port 3000...'))

// We don't have if blocks here.
// We can move some of these rounds to a different file.
// For example, we can move all the rounds related to courses to a new file: courses.js

Nodemon

我们现在每次做出改动,都需要重新运行一次node命令。使用Nodemon来进行监控,每次文件发生改变都会自动重新编译。

npn i -g nodemon

现在使用nodemon index.js来答代替node index.js

Environment Variables

看到我们的代码

app.listen(3000, () => console.log('Listening on the port 3000...'))

端口3000是写死的,这在开发环境还可以用,但到了生产环境就不凑效了,因为端口是由平台动态分配的,所以我们要使用环境变量。

基本上Node环境的共享平台,环境变量中管理端口的属性是PORT。

环境变量就是在进程运行时侯才产生的变量,是在应用之外设置的变量。

使用process来访问,如果设置了环境变量,就使用,否则为3000。

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

现在使用nodemon,会发现监听的是3000端口。现在来设置环境变量。

在Mac使用export,Windows为set, 在Terminal输入 set PORT=5000。此时就会发现监听的是5000端口了。

Route Parameters

之前我们实现了获取所有课程的API,现在来做获取单一课程的API。

如果要在获取单一课程,我们应该在URL中包含课程信息,如**/api/courses/1**。

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

现在我们访问 localhost:3000/api/courses/1,就会发现其返回了一个1。如果不止一个参数呢?

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

此时访问,就会返回一个对象,里面包含了year和month属性,其value则为URL内的值。

使用这种表达式,也可以读取查询字符串**(Query String Parameters)**,也就是跟在问号后的参数

// We use parameters for essiential or required value,
// and query string parameters for anything that is optional

app.get("/api/posts/:year", (req, res) => {
  res.send(req.query); //------Visit /api/posts/2018?sortBy=name (Notice the '?' before 'sortBy') --->  {"sortBy":"name"}
});

Handling GET Requests

现在来完成获取单一课程的API。

const courses = [
  { id: 1, name: "courses1" },
  { id: 2, name: "courses2" },
  { id: 3, name: "courses3" },
];

app.get("/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 the given ID was not found.");
  res.send(course);
});

有课程则返回对应的课程对象,如果没有,则返回404和提示信息。

Handling POST Requests

我们现在来创建一个POST请求,来新建一个课程。

在POST请求中,我们需要读取request的请求体**(Body)**以获得课程对象,使用新的属性来创建新的课程。

当我们在处理request.body.name的时候,我们要打开Express获取请求体(Body)种JSON对象的功能,这默认是关闭的。

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

app.use(express.json()); // Enable parsing of JSON object

在这里我们其实是添加了一个中间件。当我们使用express.json()这个方法的时候,它返回一个中间件,然后使用use方法在处理请求的流程中使用这个中间件。

app.post("/api/courses", (req, res) => {
  const course = {
    id: courses.length + 1,
    name: req.body.name, // Assume there is an object in req.body, and the object has a property 'name',
    // In order for this line to work, we need to enable parsing of JSON object in the body of request (Whiich is disable by default).
  };
  courses.push(course);
  res.send(course);
});

Calling Endpoints Using Postman

我们可以使用postman、APIpost来进行测试。其实对于Chrome可以安装Postman插件,Edge有Postwoman插件。

选择POST模式,自定义JSON格式数据,进行发送。在这里插入图片描述

Input Validation

如果客户端在POST的时候忘记发送了某个属性,例如上一个添加课程的请求中没有发送name属性,或者name

属性不合规,怎么办?所以我们需要验证。

**从安全角度看永远不要相信客户端发来的东西,永远都要验证。**在这里我们只有一个name属性,所以可以做一个很简单的小验证。

app.post("/api/courses", (req, res) => {
  if (!req.body.name || req.body.name.length < 3) {
    res.status(400).send('Name is required and should be minimum 3 characters');
    retuen;
  }

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

但如果有很多属性,我们不可能都这样写,所以我们使用 Joi。 (npm i joi) 我们用joi重写一次这个逻辑

首先导入

const Joi = require("joi");

使用Joi之前,需要定义一个schema,它定义了对象的外观特征,比如应该有什么属性,是不是email,是不是字符串,最大最小为多少。

// Using Joi to validate input
const schema = Joi.object({
  name: Joi.string().min(3).required(),
});
const result = schema.validate(req.body);
if (result.error) {
  res.status(400).send(result.error);
}

Handling PUT Request

我们为课程API创建一个PUT请求,首先,我们先查找是否有这个课程,如果不存在返回404。若存在,验证客户端发送的东西,不符合则发送400。如果都可以,则更新课程。

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 schema = Joi.object({
    name: Joi.string().min(3).required(),
  });
  const result = schema.validate(req.body);
  if (result.error) {
    return res.status(400).send(result.error);
  }

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

可以看到,我们复制了验证数据的逻辑,所以我们将他单独分离出来,这样就可由不只是在PUT语句里使用。

function validateCourse(course) {
  const schema = Joi.object({
    name: Joi.string().min(3).required(),
  });
  return schema.validate(course);
}
// Validate
// If invalid, return 400 - Bad Request

const { error } = validateCourse(req.body); // get the req.body.error
if (error) return res.status(400).send(error);

我们也要记得,将刚才写的POST请求的语句的验证部分也用上我们提取出来的验证函数。

Handling DELETE Request

与PUT处理逻辑相同,先查找课程,符合要求则删除课程。

app.delete("/api/courses/:id", (req, res) => {
  // Look up the course
  // Nt 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);

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

All Codes.

// index.js
const Joi = require("joi");
const express = require("express");
const app = express();

app.use(express.json()); // Enable parsing of JSON object

const courses = [
  { id: 1, name: "courses1" },
  { id: 2, name: "courses2" },
  { id: 3, name: "courses3" },
];

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

app.post("/api/courses", (req, res) => {
  const { error } = validateCourse(req.body); // get the req.body.error
  if (error) return res.status(400).send(error);
  const course = {
    id: courses.length + 1,
    name: req.body.name,
  };
  courses.push(course);
  res.send(course);
});

app.delete("/api/courses/:id", (req, res) => {
  // Look up the course
  // Nt 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);

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

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

const port = process.env.PORT || 3000;

app.listen(3000, () => console.log(`Listening on the port ${port}...`));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值