【express-generator】06-RESTFUL API设计(第二阶段)

前言:

前面我们学习了第一阶段的express-generator内容以及做了对应练习,现在我们正式开始第二阶段的学习以及练习。本篇介绍的内容是RESTFUL API设计。

第二阶段的大纲如下: 

  1. RESTful API 设计

    • 学习如何设计符合 REST 原则的 API,包括资源的表示和 HTTP 方法的使用。
  2. 路由分组

    • 学习如何使用路由分组来组织相关的路由,提高代码的可维护性。
  3. 路由重定向

    • 学习如何使用 res.redirect() 和 res.location() 来实现路由重定向。
  4. CRUD 操作

    • 练习实现完整的 CRUD 操作的路由,包括与数据库的交互。

 这一篇介绍的是RESTFUL API 设计。

一、RESTFUL 原则

1.核心概念:

  1. 无状态(Stateless)

    • 每个请求从客户端到服务器都包含所有必要的信息来理解和处理请求。服务器不会存储任何客户端请求之间的状态信息。

  2. 统一接口(Uniform Interface)

    • 通过统一的接口与资源进行交互,使得资源能够被独立地操作。资源通过 URI(统一资源标识符)进行识别,操作通过 HTTP 方法(GET、POST、PUT、DELETE 等)进行。

  3. 资源导向(Resource-Oriented)

    • API 的设计围绕资源展开,每个资源都有一个唯一的 URI。资源可以是实体(如用户、文章、评论)或其他任何可以被命名的事物。

  4. 超媒体作为应用状态的引擎(HATEOAS)

    • 客户端通过服务器提供的超媒体链接动态发现可用的动作和资源,而不是硬编码在客户端中。

  5. 分层系统(Layered System)

    • 系统可以由多个分层组成,每个分层都有不同的功能。客户端通常不知道它们是直接与服务器通信,还是与中间层(如代理、网关)通信。

2.作用

  1. 可扩展性

    • 由于无状态和分层系统的设计,RESTful API 可以轻松扩展,支持大量客户端和分布式部署。

  2. 互操作性

    • 使用标准的 HTTP 方法和 URI,使得不同语言和平台的客户端都可以与 API 交互。

  3. 简单性和一致性

    • 统一接口和资源导向的设计使得 API 更容易理解和使用。

  4. 可维护性

    • 由于 API 的设计基于资源和标准方法,代码更容易维护和扩展。

 二、如何设计 RESTFUL API

1. 定义资源

  • 确定你的应用程序中的资源(如用户、文章、评论等)。

  • 每个资源都有一个唯一的 URI。

2. 设计 URI

  • 使用有意义的 URI 来表示资源。例如:

    • /users 表示用户集合。

    • /users/1 表示 ID 为 1 的用户。

3. 使用 HTTP 方法

  • 使用标准的 HTTP 方法来操作资源:

    • GET:获取资源。

    • POST:创建新资源。

    • PUT:更新现有资源。

    • DELETE:删除资源。

4. 返回状态码和响应体

  • 使用适当的 HTTP 状态码来表示请求的结果(如 200 OK、201 Created、404 Not Found、400 Bad Request 等)。

  • 返回有意义的响应体,通常为 JSON 格式。

 三、示例练习

1.示例设计

假设我们有一个简单的用户管理系统,包含以下资源和操作:

资源:

  • 用户(/restful_users

操作:

  • 获取所有用户(GET /restful_users

  • 获取单个用户(GET /restful_users/:id

  • 创建新用户(POST /restful_users

  • 更新用户信息(PUT /restful_users/:id

  • 删除用户(DELETE /restful_users/:id

2.示例 API 设计

  1. 获取所有用户

    • URIGET /restful_users

    • 响应:返回用户列表的 JSON。

  2. 获取单个用户

    • URIGET /restful_users/:id

    • 响应:返回指定用户的 JSON。

  3. 创建新用户

    • URIPOST /restful_users

    • 请求体:包含用户信息的 JSON。

    • 响应:返回新创建的用户信息和状态码 201。

  4. 更新用户信息

    • URIPUT /restful_users/:id

    • 请求体:包含更新信息的 JSON。

    • 响应:返回更新后的用户信息。

  5. 删除用户

    • URIDELETE /restful_users/:id

    • 响应:返回状态码 204(无内容)。

 3.示例代码(跟篇练习)

3.1 创建express应用

使用express-generator脚手架创建一个express应用程序。

//安装express-generator,如已经安装则跳过。
npm install -g express-generator
//创建express应用程序,然后跟着提示往下走。
express xx
cd xx
npm install

3.2 创建模拟数据

在项目根目录下创建一个 data.json 文件,用于存储用户数据:

[
  { "id": 1, "name": "Alice", "email": "alice@example.com" },
  { "id": 2, "name": "Bob", "email": "bob@example.com" },
  { "id": 3, "name": "Tom", "email": "tom@example.com" }
]

3.3 创建工具函数

utils/tool.js 中,添加工具函数用于读取和写入数据文件,以及处理结果响应:

// utils/tool.js
const fs = require('fs');
const path = require('path');

const dataFilePath = path.join(__dirname, '../data.json');

// 读取数据
function readData() {
  const data = fs.readFileSync(dataFilePath, 'utf8');
  return JSON.parse(data);
}

// 写入数据
function writeData(data) {
  fs.writeFileSync(dataFilePath, JSON.stringify(data, null, 2));
}

// 成功响应
function successResponse(res, data, status = 200) {
  res.status(status).json({
    success: true,
    data,
    status
  });
}

// 错误响应
function errorResponse(res, message, status = 400) {
  res.status(status).json({
    success: false,
    message,
    status
  });
}

module.exports = {
  readData,
  writeData,
  successResponse,
  errorResponse,
};

3.4 路由方法

routes 文件夹下创建一个 restful_user.js 文件,用于定义用户相关的路由:

// routes/restful_user.js
const express = require('express');
const router = express.Router();
const { readData, writeData, successResponse, errorResponse } = require('../utils/tool');

// 获取所有用户
router.get('/', (req, res) => {
  const users = readData();
  successResponse(res, users);
});

// 获取单个用户
router.get('/:id', (req, res) => {
  const users = readData();
  const user = users.find((u) => u.id === parseInt(req.params.id));
  if (!user) {
    errorResponse(res, 'User not found', 404);
  } else {
    successResponse(res, user);
  }
});

// 创建新用户
router.post('/', (req, res) => {
  const { name, email } = req.body;
  if (!name || !email) {
    errorResponse(res, 'Invalid user data', 400);
  } else {
    const users = readData();
    const newUser = {
      id: users.length + 1,
      name,
      email,
    };
    users.push(newUser);
    writeData(users);
    successResponse(res, newUser, 201);
  }
});

// 更新用户信息
router.put('/:id', (req, res) => {
  const users = readData();
  const userIndex = users.findIndex((u) => u.id === parseInt(req.params.id));
  if (userIndex === -1) {
    errorResponse(res, 'User not found', 404);
  } else {
    const { name, email } = req.body;
    if (name) users[userIndex].name = name;
    if (email) users[userIndex].email = email;
    writeData(users);
    successResponse(res, users[userIndex]);
  }
});

// 删除用户
router.delete('/:id', (req, res) => {
  const users = readData();
  const userIndex = users.findIndex((u) => u.id === parseInt(req.params.id));
  if (userIndex === -1) {
    errorResponse(res, 'User not found', 404);
  } else {
    const deletedUser = users.splice(userIndex, 1)[0];
    writeData(users);
    successResponse(res, deletedUser, 204);
  }
});

module.exports = router;

3.5 引入和使用

app.js 中引入 restful_user.js 路由模块,并将其挂载到/restful_user路径下

3.6 API测试

 我在bin/www中修改了端口号

以及添加了一条服务器启动的打印语句

(1)启动服务器:nodemon npm start
(2)打开apipost工具
-1 获取所有用户
  • URIGET /restful_users

  • 响应:返回用户列表的 JSON。

-2 获取单个用户
  • URIGET /restful_users/:id

  • 响应:返回指定用户的 JSON。

-3 添加用户数据
  • URIPOST /restful_users

  • 请求体:包含用户信息的 JSON。

  • 响应:返回新创建的用户信息和状态码 201。

我们可以在data.json中看到新增的用户数据

-4 修改用户数据
  • URIPUT /restful_users/:id

  • 请求体:包含更新信息的 JSON。

  • 响应:返回更新后的用户信息。

 这里我们将新增加的用户数据(id为4),将name从“张三”改为“张三四五”

data.json中的数据页也会对应修改。 

 

假设我们修改的是不存在的id数据,(此时data.json只有四条数据)响应结果则如下:

-5 删除用户数据
  • URIDELETE /restful_users/:id

  • 响应:返回状态码 204(无内容)。

 

 此时id为1的用户数据已被删除。

 如果我们对不存在的id数据进行删除,响应结果如下:


至此,本篇文章到此结束,有不清楚的地方,欢迎留言评论~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/958744.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 预训练:打通视觉与大语言模型应用壁垒——Python预训练视觉和大语言模型

大语言模型是一种由包含数百亿甚至更多参数的深度神经网络构建的语言模型,通常使用自监督学习方法通过大量无标签文本进行训练,是深度学习之后的又一大人工智能技术革命。 大语言模型的发展主要经历了基础模型阶段(2018 年到2021年)、能力探索阶段(2019年…

【深度学习】2.视觉问题与得分函数

计算机视觉任务 可以通过神经网络搜索是什么类别的动物。 图像实际就是含有数值的三维矩阵。 像素值从0-255可以表示亮度递增的参数。数字越大,像素点越亮。 最后的3表示三个颜色通道,常见的如JPG、RGB等。 现实场景容易发生各种遮蔽现象。 计算机判断…

1.CSS的三大特性

css有三个非常重要的三个特性&#xff1a;层叠性、继承性、优先级 1.1 层叠性 想通选择器给设置想听的样式&#xff0c;此时一个样式就会覆盖&#xff08;层叠&#xff09;另一个冲突的样式。层叠性主要是解决样式冲突的问题。 <!DOCTYPE html> <html lang"en&…

使用Edge打开visio文件

使用Edge打开visio文件 打开Edge浏览器搜索‘vsdx edge’ 打开第一个搜索结果 Microsoft Support 根据上述打开的页面进行操作 第一步&#xff1a;安装Visio Viewer 第二步&#xff1a;添加注册表 桌面新增文本文件&#xff0c;将下面的内容放入新建文本中&#xff0c;修…

基于微信小程序的健身管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

GPS信号生成:C/A码序列生成【MATLAB实现】

GPS C/A码序列生成【MATLAB实现】 在本文中&#xff0c;将简要介绍GPS C/A码及其生成原理&#xff0c;并且用MATLAB代码实现。 GPS信号与C/A码 GPS的信号主要有三类&#xff1a;载波&#xff08;carrier&#xff09;、测距码&#xff08;也可以说是伪随机噪声码&#xff0c;…

redis离线安装部署详解(包括一键启动)

像上文一样 因为在学习的过程中没有查到一个详细的离线部署方案 所以在自己学习之后想要自己写一个文章 希望可以帮助后续学习redis离线部署的朋友少走一线弯路 首先就是下载安装包 可以自己在本地下载再传到机器上&#xff08;通过xftp或lrzsz都可&#xff09; http://d…

Hadoop•搭建完全分布式集群

听说这里是目录哦 一、安装Hadoop&#x1f955;二、配置Hadoop系统环境变量&#x1f96e;三、验证Hadoop系统环境变量是否配置成功&#x1f9c1;四、修改Hadoop配置文件&#x1f36d;五、分发Hadoop安装目录&#x1f9cb;六、分发系统环境变量文件&#x1f368;七、格式化HDFS文…

安卓动态设置Unity图形API

命令行方式 Unity图像api设置为自动,安卓动态设置Vulkan、OpenGLES Unity设置 安卓设置 创建自定义活动并将其设置为应用程序入口点。 在自定义活动中,覆盖字符串UnityPlayerActivity。updateunitycommandlineararguments (String cmdLine)方法。 在该方法中,将cmdLine…

Java春招面试指南前言

在当今竞争激烈的就业市场中&#xff0c;对于即将踏入职场的Java开发者而言&#xff0c;春招是一次宝贵的机会。本博客专栏旨在为大家提供一份全面且实用的Java春招面试指南&#xff0c;助力大家顺利通过面试&#xff0c;开启职业生涯的新篇章。 无论你是初出茅庐的应届生&…

记录一次k8s起不来的排查过程

我在k8s集群&#xff0c;重启了一个node宿主机&#xff0c;竟然发现kubelet起不来了&#xff01;报错如下 这个报错很模糊&#xff0c;怎么排查呢。这样&#xff0c;开两个界面&#xff0c;一个重启kubelet&#xff0c;一个看系统日志(/var/log/message:centos&#xff0c;/va…

利用Qt5.15.2编写Android程序时遇到的问题及解决方法

文章目录 背景1.文件读写 背景 目前我用的是Qt5.15.2来编写Qt程序&#xff0c;环境的配置看我这篇文章【Qt5.15.2配置Android开发环境】 项目中的一些配置的截图&#xff1a; 1.文件读写 假如直接用 QFileDialog::getExistingDirectory来获取路径的话&#xff0c;会得到类…

JVM深入学习(一)

目录 一.JVM概述 1.1 为什么要学jvm&#xff1f; 1.2 jvm的作用 1.3 jvm内部构造 二.JVM类加载 2.1类加载过程 2.2类加载器 2.3类加载器的分类 2.4双亲委派机制 三.运行时数据区 堆空间区域划分&#xff08;堆&#xff09; 为什么分区(代)&#xff1f;&#xff08…

o1 医学推理:基于推断时长扩展与旅程学习,仅用 500 条蒸馏示例,实现 6%~11% 性能提升

o1 医学推理&#xff1a;基于推断时长扩展与旅程学习&#xff0c;仅用 500 条蒸馏示例&#xff0c;实现 6%&#xff5e;11% 性能提升 论文大纲1. 提出背景是什么&#xff1f;2. 概念的性质是什么&#xff1f;是什么导致这个性质&#xff1f;3. 请举一个正例、一个反例&#xff…

微信小程序隐藏右侧胶囊按钮,分享和关闭即右侧三个点和小圆圈按钮

在微信小程序开发过程中&#xff0c;可能需要将右侧的胶囊按钮、即右侧的三个点和小圆圈按钮关闭掉。如图&#xff1a; 这时&#xff0c;我们只需在该页面的json文件中进行相关配置即可 {"navigationBarTitleText": "商品详情页","navigationStyle&q…

chrome小插件:长图片等分切割

前置条件&#xff1a; 安装有chrome谷歌浏览器的电脑 使用步骤&#xff1a; 1.打开chrome扩展插件 2.点击管理扩展程序 3.加载已解压的扩展程序 4.选择对应文件夹 5.成功后会出现一个扩展小程序 6.点击对应小程序 7.选择图片进行切割&#xff0c;切割完成后会自动保存 代码…

检测到联想鼠标自动调出运行窗口,鼠标自己作为键盘操作

联想鼠标会自动时不时的调用“运行”窗口 然后鼠标自己作为键盘输入 然后打开这个网页 &#xff08;不是点击了什么鼠标外加按键&#xff0c;这个鼠标除了左右和中间滚轮&#xff0c;没有其他按键了&#xff09;

深入MapReduce——计算模型设计

引入 通过引入篇&#xff0c;我们可以总结&#xff0c;MapReduce针对海量数据计算核心痛点的解法如下&#xff1a; 统一编程模型&#xff0c;降低用户使用门槛分而治之&#xff0c;利用了并行处理提高计算效率移动计算&#xff0c;减少硬件瓶颈的限制 优秀的设计&#xff0c…

【大数据】机器学习----------强化学习机器学习阶段尾声

一、强化学习的基本概念 注&#xff1a; 圈图与折线图引用知乎博主斜杠青年 1. 任务与奖赏 任务&#xff1a;强化学习的目标是让智能体&#xff08;agent&#xff09;在一个环境&#xff08;environment&#xff09;中采取一系列行动&#xff08;actions&#xff09;以完成一个…

Android中Service在新进程中的启动流程

目录 1、Service与AMS交互框架介绍 1.1、认识AMS代表IActivityManager 1.2、认识客户端代表IApplicationThread 2、Service启动流程概览 我们知道Android有四大组件&#xff0c;Activity、Service、ContentProvider、Broadcast&#xff0c;每个组件在系统运行中或者我们编写…