简单用Express写一下接口

5,748 阅读11分钟

Express

express 介绍

  • Express 是一个第三方模块,用于快速搭建服务器(替代http模块)
  • Express 是一个基于 Node.js 平台,快速、开放、极简的 web 开发框架。同类型产品 Koa
  • Express保留了http模块的基本API,使用express的时候,也能使用http的API
    • 使用express的时候,仍然可以使用http模块的方法,比如 res.end()、req.url
  • express还额外封装了一些新方法,能让我们更方便的搭建服务器
  • express提供了中间件功能,其他很多强大的第三方模块都是基于express开发的
  • Express 官网

安装 express

项目文件夹中,执行 npm i express。即可下载安装express。

注意:express不能安装在express文件夹中。否则安装失败。

使用Express构造Web服务器

使用Express构建Web服务器步骤:

1) 加载 express 模块

2) 创建 express 服务器

3) 开启服务器

4) 监听浏览器请求并进行处理
// 使用express 搭建web服务器
// 1) 加载 express 模块
const express = require('express');

// 2) 创建 express 服务器
const app = express();

// 3) 开启服务器
app.listen(3000, () => console.log('express服务器开始工作了'));

// 4) 监听浏览器请求并进行处理

app.get('GET请求的地址', 处理函数);

app.post('POST请求的地址', 处理函数);

express封装的方法

express之所以能够实现web服务器的搭建,是因为其内部对核心模块http进行了封装。

封装之后,express提供了非常方便好用的方法。

比如前面用到的 app.get() 和 app.post() 就是express封装的新方法。

下面再介绍一个 res.send() 方法

  • 该方法可以代替原生http模块中的 res.end 方法,而且比 res.end 方法更好用
  • res.send() 用于做出响应
  • 响应的内容同样不能为数字
  • 如果响应的是JS对象,那么方法内部会自动将对象转成JSON格式。
  • 而且会自动加Content-Type响应头
  • 如果已经做出响应了,就不要再次做出响应了。
const express = require('express');
const app = express();
app.listen(3000, () => console.log('启动了'));

// 写接口
app.get('/api/test', (req, res) => {
  // res.end('hello world,哈哈哈'); // 响应中文会乱码,必须自己加响应头
  // res.end(JSON.stringify({ status: 0, message: '注册成功' })); // 只能响应字符串或者buffer类型

  // express提供的send方法,可以解决上面的两个问题
  res.send({ status: 0, message: '注册成功' }); // send方法会自动设置响应头;并且会自动把对象转成JSON字符串
});

使用 nodemon 命令运行上述代码后,即可使用 http://localhost:3000/api/test 请求接口了。

全局模块 nodemon

当服务器代码修改后,就要重新启动服务,非常麻烦。

nodemon 是一个全局模块,安装后,可以使用 nodemon 代替 node 运行js文件。

好处是,当代码保存后,nodemon 会检测文件代码是否改变了,如果改变了,就会自动重启服务器。

全局安装nodemon

npm i nodemon -g

使用

nodemon xxx.js

使用nodemon 命令代替node命令,我们就可以专心开发代码了,而不用但是服务有没有重启的问题了。

建议不要滥用nodemon,建议只在启动服务时使用nodemon。其他情况继续使用node。

接口测试工具

上述使用浏览器测试接口,只能测试GET方式的接口。如果是POST方式的接口,则无法使用浏览器测试。

所以就需要一个专业的测试接口的工具。

常用的的接口测试工具:

  • Postman (默认英文版本,github上有中文汉化包)
  • Apifox(默认中文)

两个软件用法类似,且都比较简单。下面拿Apifox说明。

  • 首先,登录Apifox,没有账号的需要先注册

  • 点击侧边栏的“项目”,创建一个新的项目

  • 在“项目”页,选项进入该项目接口

1646658245644.png

1646658268332.png

发送GET请求

1646658093112.png

发送POST请求

1646658179579.png

写接口

写接口的目的是体会后端的接口是怎么写的。所以写最简单的接口即可。

  • 创建后端接口项目文件夹,比如 code
  • 下载安装所需模块 express
  • 我们可以使用 then-fs 进行封装。
  • 约定,每一个方法都返回Promise对象(为了方便,查询方法query可以返回查询数据)
  • 后期,就可以通过 .then 或者 async和await 获取结果了。
  • 这样,就可以知道Promise如何使用了。
  npm i fs-then

创建index.js

创建index.js,作用是编写后端代码,启动服务。

使用的模块为 express。

引入fs-then。

初始代码,可以去官网复制并修改

// 用于启动服务,并且写接口
import express from "express";
const app = express()
import fs from "fs-then";

// 写接口
// app.请求方式('接口地址', '带有req、res参数的处理函数');
// app.get('/api/student', (req, res) => {});
// app.post();
// app.delete();
// app.put();


app.listen(3000, () => console.log('启动了'));

只需这几行,就可以使用 nodemon index.js 启动服务了。

封装操作JSON的模块

  • 准备好存储数据的 json文件(数据.json)。
  • 准备好处理JSON文件的自定义模块。

准备JSON文件

真实开发中,数据一般都放在数据库中存储,作为前端程序员,大部分都不了解数据库。

所以,我们可以使用JSON文件存储数据。(使用JSON文件代替数据库,以便完成后续的开发)

创建 数据.json ,文件内容如下:

[
  {
    "id": 1,
    "bookname": "西游记",
    "author": "吴承恩",
    "publisher": "北京出版社"
  },
  {
    "id": 2,
    "bookname": "红楼梦",
    "author": "曹雪芹",
    "publisher": "上海出版社"
  },
  {
    "id": 5,
    "bookname": "水浒传",
    "author": "施耐庵",
    "publisher": "清华出版社"
  },
  {
    "id": 12,
    "bookname": "三国演义",
    "author": "罗贯中",
    "publisher": "商务出版社"
  }
]

获取图书接口

  • 接口名称:/api/getbooks
  • 请求方式:GET
  • 请求参数:无
  • 响应数据:{ status: 0, message: '获取图书成功', data: [ { 一本书 }, { 一本书 } ] }

基本代码如下:

app.get('/api/getbooks', async (req, res) => {
    // 如果要使用async来实现同步的话,要记住await后面要promise对象才可以,用第三方模块fs-then,是把已经封装好的promise直接调用,用fs内置模块得自己封装promise
    const data = await fs.readFile('./数据.json', 'utf-8')
    res.send({ status: 0, message: '获取图书成功', data: JSON.parse(data) })
})

接下来,可以使用客户端发送请求检测了。

1646658321511.png

根据id来获取数据接口

  • 接口名称:/api/onebook
  • 请求方式:GET
  • 请求体:id(ID)
  • Content-Type: application/x-www-form-urlencoded
  • 响应数据:{ status: 0, message: '获取图书成功' }

基本代码如下:

   app.get('/api/onebook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 获取到传进来的id参数
    const id = req.query.id
    // 通过数组的find方法查询对应数据
    const book = data.find(function (item) {
        return item.id == id
    })
    if (book) {
        res.send({ status: 0, message: '获取图书成功', data: book })
    } else {
        res.send({ status: 0, message: '获取图书失败' })
    }
})

添加图书接口

  • 接口名称:/api/addbook
  • 请求方式:POST
  • 请求体:bookname (书名)、author(作者)、publisher(出版社)
  • Content-Type: application/x-www-form-urlencoded
  • 响应数据:{ status: 0, message: '添加图书成功' }

基本代码如下:

// 配置,接收请求体
app.use(express.urlencoded({ extended: true })); // 接收查询字符串格式请求体


app.post('/api/addbook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 已经设置app.use(express.urlencoded()),可以直接用req.query来获取前端传进来的参数
    // 获取到的参数是有多个数据的数组,所以需要用展开运算符...展开,不然无法识别会报错
    // Date.now()是从1970年1月1日到现在的时间戳
    data.push({ ...req.body, id: Date.now() })

    // writeFile是创建文件,如果没有这个文件名会增加新的,已经有这个文件会直接覆盖,目的就是为了把增加新数据后的文件覆盖掉之前的
    await fs.writeFile('./数据.json', JSON.stringify(data))
    res.send({ status: 0, message: '添加图书成功' })
})

接下来,可以使用客户端发送请求检测了。

1646658345270.png

修改图书接口

  • 接口名称:/api/updatebook
  • 请求方式:PUT
  • 请求体:id(ID)、bookname (书名)、author(作者)、publisher(出版社)
  • Content-Type: application/x-www-form-urlencoded
  • 响应数据:{ status: 0, message: '修改图书成功' }

所以:


app.put('/api/updatebook', async (req, res) => {
        // 先获取数据
        let data = await fs.readFile('./数据.json', 'utf-8')
        // 然后把数据转换成数组
        data = JSON.parse(data)
        // 修改一样是根据id来修改,所以要先获取id
       const index = data.findIndex(function (item) {
        // 判断请求参数中的id和数据中的id相同的数据是第几个  
        return req.body.id == item.id
    })
        // 如果上面的索引找不到,会返回-1,需要进行个判断
    if (index == -1) {
        res.send({ status: 1, message: 'id不存在' })
    } else {
        // 把对应索引的数据改成传进来的参数
        data[index] = req.body
        // 把新数据覆盖之前的数据
        await fs.writeFile('./数据.json', JSON.stringify(data))
        res.send({ status: 0, message: '修改图书成功' })
    }
})

接下来,可以使用客户端发送请求检测了。

1646658381983.png

删除图书接口

  • 接口名称:/api/delbook
  • 请求方式:DELETE
  • 请求参数:id(ID)
  • 响应数据:{ status: 0, message: '删除图书成功' }

所以:

app.delete('/api/delbook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 删除一样是根据id来删除,所以要先获取id
    const index = data.findIndex(function (item) {
        // 判断请求参数中的id和数据中的id相同的数据是第几个
        return req.query.id == item.id
    })
    // 用splice删除对应数据
    data.splice(index, 1)

    // 把新数据覆盖之前的数据
    await fs.writeFile('./数据.json', JSON.stringify(data))

    // 响应
    res.send({ status: 0, message: '删除图书成功' })
})

接下来,可以使用客户端发送请求检测了。

使用前端代码测试接口

前端发送Ajax请求,请求接口。会有跨域问题。

这里可以使用CORS方案解决跨域。

后端

  • 安装 cors 模块。
 npm i cors
  • 代码中,在所有接口之前,加载cors,并注册为中间件。
  import cors from "cors";
  app.use(cors())

完整代码

// 引入express模块 --> 用来快速开发接口
import express from "express";
const app = express()
// 引入cors模块 --> 用来解决跨域的
import cors from "cors";
// 引入封装好promise的fs-then模块
import fs from "fs-then";
// 引入修改编码格式的body-parser模块 --> 要先下载  body-parser模块名字
import bodyParser from "body-parser";

// 设置express 支持post请求参数格式支持json   支持application/json
app.use(bodyParser.json());

// 设置 express 支持 post请求参数 默认是application/x-www-form-urlencoded编码格式  app.use(express.urlencoded());
app.use(express.urlencoded())

// 解决跨域问题
app.use(cors())



// 获取图书接口(get) --> 路径+函数
app.get('/api/getbooks', async (req, res) => {
    const data = await fs.readFile('./数据.json', 'utf-8')
    res.send({ status: 0, message: '获取图书成功', data: JSON.parse(data) })
})


// 根据id来获取数据接口
app.get('/api/onebook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 获取到传进来的id参数
    const id = req.query.id
    // 通过数组的find方法查询对应数据
    const book = data.find(function (item) {
        return item.id == id
    })
    if (book) {
        res.send({ status: 0, message: '获取图书成功', data: book })
    } else {
        res.send({ status: 0, message: '获取图书失败' })
    }
})


// 新增接口(post)
app.post('/api/addbook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 已经设置app.use(express.urlencoded()),可以直接用req.query来获取前端传进来的参数
    // 获取到的参数是有多个数据的数组,所以需要用展开运算符...展开,不然无法识别会报错
    // Date.now()是从1970年1月1日到现在的时间戳
    data.push({ ...req.body, id: Date.now() })

    // writeFile是创建文件,如果没有这个文件名会增加新的,已经有这个文件会直接覆盖,目的就是为了把增加新数据后的文件覆盖掉之前的
    await fs.writeFile('./数据.json', JSON.stringify(data))
    res.send({ status: 0, message: '添加图书成功' })
})


// 删除接口
app.delete('/api/delbook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 删除一样是根据id来删除,所以要先获取id
    const index = data.findIndex(function (item) {
        // 判断请求参数中的id和数据中的id相同的数据是第几个
        return req.query.id == item.id
    })
    // 用splice删除对应数据
    data.splice(index, 1)

    // 把新数据覆盖之前的数据
    await fs.writeFile('./数据.json', JSON.stringify(data))

    // 响应
    res.send({ status: 0, message: '删除图书成功' })
})


// 修改接口
app.put('/api/updatebook', async (req, res) => {
    // 先获取数据
    let data = await fs.readFile('./数据.json', 'utf-8')
    // 然后把数据转换成数组
    data = JSON.parse(data)
    // 修改一样是根据id来修改,所以要先获取id
    const index = data.findIndex(function (item) {
        // 判断请求参数中的id和数据中的id相同的数据是第几个  
        return req.body.id == item.id
    })
    // 如果上面的索引找不到,会返回-1,需要进行个判断
    if (index == -1) {
        res.send({ status: 1, message: 'id不存在' })
    } else {
        // 把对应索引的数据改成传进来的参数
        data[index] = req.body
        // 把新数据覆盖之前的数据
        await fs.writeFile('./数据.json', JSON.stringify(data))
        res.send({ status: 0, message: '修改图书成功' })
    }
})


// 开启服务器 --> 端口+处理函数
app.listen(3000, () => {
    console.log('3000 服务器开启成功');
})

前端

正常发送请求即可。

<script src="./axios.js"></script>
<script>
  axios.get('http://localhost:3000/api/getbooks').then(result => {
    console.log(result);
  });
</script>

1646741638793.png

补充

axios文件我是直接复制源码封装起来的

复制下面的代码到html页面,按住ctrl+鼠标左键点击js链接,进入浏览器把全部源码复制一下,新建文件夹封装起来引入就行

<script src="https://cdn.staticfile.org/axios/1.1.3/axios.js"></script>

image.png

image.png

展示页面

新建list.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        li {
            list-style: none;
        }

        table {
            width: 500px;
            height: 250px;
            margin: 100px;
            text-align: center;

        }

        button {
            cursor: pointer;
        }

        .add {
            width: 250px;
            height: 150px;
            border: 1px solid #000;
            margin-top: 100px;
        }

        .add input {
            width: 150px;
        }

        .add .row {
            height: 30px;
        }

        .btnedit a {
            text-decoration: none;
            color: #000;
        }
    </style>
</head>

<body>
    <div style="display: flex;">
        <div>
            <table border="" cellspacing="0" cellpadding="0">
                <thead>
                    <th>id</th>
                    <th>书名</th>
                    <th>作者</th>
                    <th>出版社</th>
                    <th colspan="2">操作</th>
                    <!-- <th>操作</th> -->
                </thead>
                <tbody>
                    <!-- <tr>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr> -->

                </tbody>
            </table>
        </div>

        <div class="add">
            <div class="row"><label for="">书名</label><input class="bookname" type="text"></div>
            <div class="row"><label for="">作者</label><input class="author" type="text"></div>
            <div class="row"><label for="">出版社</label><input class="publisher" type="text"></div>
            <div><button class="btnadd">新增</button></div>
        </div>
        <a href=""></a>
    </div>
</body>
<script src="./js/axios.js"></script>
<!-- <script src="https://cdn.staticfile.org/axios/1.1.3/axios.js"></script> -->
<script>
    let tbody = document.querySelector('tbody')
    function init() {
        axios.get('http://127.0.0.1:3000/api/getbooks').then(res => {
            console.log(res);
            let list = res.data.data
            // console.table(list);
            let str = ''
            list.forEach((value, index) => {
                str += `<tr>
                    <td>${index + 1}</td>
                    <td>${value.bookname}</td>
                    <td>${value.author}</td>
                    <td>${value.publisher}</td>
                    <td><button class="btndel" data-id="${value.id}" >删除</button></td>
                    <td><button class="btnedit"><a href="./editList.html?id=${value.id}" >编辑</a></button></td>
                </tr>`
            })
            tbody.innerHTML = str
        })
    }
    init()

    tbody.addEventListener('click', function (e) {
        if (e.target.className == 'btndel') {
            axios.delete('http://127.0.0.1:3000/api/delbook', { id: e.target.dataset.id }).then(res => {
                if (res.data.status == 0) {
                    alert(res.data.message);
                    init()
                } else {
                    alert('删除图书失败');
                }
            })
        }
    })

    document.querySelector('.btnadd').addEventListener('click', function () {
        let bookname = document.querySelector('.bookname').value
        let author = document.querySelector('.author').value
        let publisher = document.querySelector('.publisher').value

        axios({
            url: 'http://127.0.0.1:3000/api/addbook',
            method: 'post',
            data: {
                bookname, author, publisher
            }
        }).then(res => {
            if (res.data.status == 0) {
                alert(res.data.message);
                init()
            } else {
                alert('删除图书失败');
            }
        })
    })
</script>

</html>

新建editList.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1>编辑页面</h1>
    <div class="main">
        <div>
            <label for="">书名</label> <input class="bookname" type="text" />
        </div>
        <div><label for="">作者</label> <input class="author" type="text" /></div>
        <div>
            <label for="">出版社</label> <input class="publisher" type="text" />
        </div>
        <div>
            <button>编辑</button>
        </div>
    </div>
    <script src="./js/axios.js"></script>
    <script>
        // 先把传过来的id获取到--> 使用location.search方法可以获取到连接到后面的字符串参数,不知道方法可以直接打印location
        const data = location.search
        const id = data.split('=')[data.split('=').length - 1]
        // console.log(id);
        axios({
            url: 'http://127.0.0.1:3000/api/onebook',
            params: {
                id
            }
        }).then(res => {
            let list = res.data.data
            const bookname = document.querySelector('.bookname')
            const author = document.querySelector('.author')
            const publisher = document.querySelector('.publisher')

            // console.log(bookname, author, publisher, list);

            bookname.value = list.bookname
            author.value = list.author
            publisher.value = list.publisher

            document.querySelector('button').addEventListener('click', function () {
                axios({
                    method: 'put',
                    url: 'http://127.0.0.1:3000/api/updatebook',
                    data: {
                        id,
                        bookname: bookname.value,
                        author: author.value,
                        publisher: publisher.value,
                    }
                }).then(res => {
                    // console.log(res);
                    if (res.data.status == 0) {
                        alert(res.data.message);
                        location.href = './list.html'
                    } else {
                        alert('修改图书失败');
                    }
                })
            })
        })
    </script>
</body>

</html>

最后,不建议用中文起文件名,嘿嘿