随着互联网的发展和应用场景的多样化,API 的设计变得越来越重要。其中,RESTful API 是一种最常用、最基础的 API 设计风格。本文将从 URI 开始,详细探讨如何设计 RESTful API,并提供一些相关示例代码。
URI 是什么?
URI(Uniform Resource Identifier,统一资源标识符)是一个字符串序列,用于标识互联网上的资源。URI 包含两个重要的子集:URL(Uniform Resource Locator,统一资源定位符)和 URN(Uniform Resource Name,统一资源名称)。其中,URL 指定了资源的位置,而 URN 指定了资源的名称。
RESTful API 的设计原则
在理解 URI 后,我们可以开始考虑如何设计 RESTful API。根据 Roy Fielding 的博士论文,RESTful API 应该遵循以下几个设计原则:
- 客户端-服务器架构:客户端和服务器之间应该有明确的分离,使得它们可以独立地演化和扩展。
- 无状态:服务器不应该存储客户端的状态,每个请求都应该包含足够的信息来让服务器理解。
- 缓存:服务器应该尽可能地可缓存,以减少网络传输。
- 统一的接口:RESTful API 应该采用统一的接口,包括资源的标识、资源的操作、自描述消息和超媒体作为应用状态转移的引擎(HATEOAS)。
- 按需代码:服务器应该只需要传输客户端所需要的数据。
URI 的设计
在 RESTful API 中,URI 是资源的唯一标识符,其设计应该遵循以下几个原则:
- URI 应该体现资源的层次结构,具有清晰、简洁的结构,便于客户端使用。
- URI 应该使用名词而非动词,即 URL 应该是对资源的描述,而非对动作的描述。
- URI 应该使用小写字母,横线分割单词,以提高可读性。
- URI 最好是永久的,不可改变的,避免客户端的混乱。
- URI 应该是唯一的,以避免歧义和冲突。
以下是一些 URI 设计的示例。
GET a user's profile
GET /users/{id}
CREATE a new user
POST /users
UPDATE a user's information
PUT /users/{id}
DELETE a user
DELETE /users/{id}
RESTful API 的响应
在使用 RESTful API 时,服务器应该返回一些有意义的信息,以帮助客户端理解。以下是 RESTful API 响应的各个部分。
1. 状态码
状态码用于表示请求的处理结果。以下是一些常见的状态码:
- 200 OK:请求成功
- 201 Created:请求成功,并且服务器创建了新的资源
- 400 Bad Request:请求无效,缺少必需的参数
- 401 Unauthorized:请求需要用户身份验证,但未提供有效的凭据
- 403 Forbidden:请求被禁止,没有足够的权限访问资源
- 404 Not Found:请求的资源不存在
- 500 Internal Server Error:服务器发生错误
2. 响应头
响应头包含一些元数据,如内容类型、过期时间和缓存控制等。
3. 响应体
响应体包含实际的数据,一般使用 JSON 或 XML 格式。
RESTful API 的示例代码
以下是一个使用 Node.js 和 Express.js 实现的 RESTful API 示例代码。这个示例实现了一个简单的用户管理系统。
安装 Node.js 和 Express.js
npm install --save express bcryptjs body-parser jsonwebtoken mongoose nodemon
创建 Express.js 应用
// javascriptcn.com 代码示例 const express = require("express"); const mongoose = require("mongoose"); const bodyParser = require("body-parser"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); mongoose.connect("mongodb://localhost:27017/users", { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, }); const UserSchema = mongoose.Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true }, }); const UserModel = mongoose.model("User", UserSchema); // 加密密码 const hashPassword = async (password) => { const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(password, salt); return hash; }; // 生成 Token const generateToken = (user) => { return jwt.sign({ username: user.username }, "secret"); }; // 验证 Token const authenticateToken = (req, res, next) => { const authHeader = req.headers["authorization"]; const token = authHeader && authHeader.split(" ")[1]; if (token == null) return res.sendStatus(401); jwt.verify(token, "secret", (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); }); }; // 创建用户 app.post("/api/users", async (req, res) => { const { username, password } = req.body; const hash = await hashPassword(password); const newUser = new UserModel({ username, password: hash }); try { const user = await newUser.save(); res.status(201).json(user); } catch (error) { res.status(400).json({ error: error.message }); } }); // 获取用户列表 app.get("/api/users", authenticateToken, async (req, res) => { try { const users = await UserModel.find(); res.json(users); } catch (error) { res.json({ error: error.message }); } }); // 获取指定用户 app.get("/api/users/:id", async (req, res) => { const id = req.params.id; try { const user = await UserModel.findById(id); if (user) { res.json(user); } else { res.status(404).json({ error: "User not found" }); } } catch (error) { res.status(400).json({ error: error.message }); } }); // 更新指定用户 app.put("/api/users/:id", async (req, res) => { const id = req.params.id; const { username, password } = req.body; const hash = await hashPassword(password); try { const user = await UserModel.findByIdAndUpdate( id, { username, password: hash }, { new: true } ); if (user) { res.json(user); } else { res.status(404).json({ error: "User not found" }); } } catch (error) { res.status(400).json({ error: error.message }); } }); // 删除指定用户 app.delete("/api/users/:id", async (req, res) => { const id = req.params.id; try { const user = await UserModel.findByIdAndDelete(id); if (user) { res.json(user); } else { res.status(404).json({ error: "User not found" }); } } catch (error) { res.status(400).json({ error: error.message }); } }); // 启动 Express.js 应用 app.listen(3000, () => { console.log("Server started on port 3000"); });
总结
本文介绍了从 URI 到 RESTful API 的设计之路。只有遵循良好的设计原则和规范,才能创建出易于维护和扩展的高质量 API。希望读者能够通过本文学习到 RESTful API 的基本设计方法和一些最佳实践,并在实践中掌握相关技术。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6546ff977d4982a6eb164819