【全栈修炼】RESTful架构及实践 修炼宝典

简介: 【全栈修炼】RESTful架构及实践 修炼宝典


一、概念介绍

1. REST 概念

REST:(Representational State Transfer)即表现层状态转换,定义了资源的通用访问格式,是一种网络应用程序的设计风格开发方式

在概念中,需要理解以下几个名称:

  1. 资源(Resource)

服务器上获取到的东西任何资源,一条用户记录,一个用户的密码,一张图片等等都是。

  1. 资源的表述(Representation)

资源格式,是 HTML、XML、JSON、纯文本、图片等等,可以用各种各样的格式来表述你获取到的资源。

  1. 状态转移(State Transfer)

URL定位资源,用 HTTP 动词(GET,POST,DELETE,DETC)描述操作。操作是动词,资源是名词。

  1. 统一接口(Uniform Interface)

即通过统一的接口对资源进行操作。

2. REST 特点

REST 通常基于使用 HTTPURI ,和 XML 以及 HTML 这些现有的广泛流行的协议和标准,每一种 URI 代表一种资源。

REST 通常使用 JSON 数据格式。

REST 基本架构的四个方法:

  • GET - 用于获取数据
  • PUT - 用于更新或添加数据
  • DELETE - 用于删除数据
  • POST - 用于添加数据

下面会通过一个场景介绍。

3. REST 优点

  • 可更高效利用缓存来提高响应速度
  • 通讯本身的无状态性可以让不同的服务器的处理一系列请求中的不同请求,提高服务器的扩展性
  • 浏览器即可作为客户端,简化软件需求
  • 相对于其他叠加在HTTP协议之上的机制,REST的软件依赖性更小
  • 不需要额外的资源发现机制
  • 在软件技术演进中的长期的兼容性更好

二、实例介绍

REST 定义了资源的通用访问格式,接下来一个消费者为实例,介绍 RESTful API 定义:

  1. 获取所有 users
GET /api/users
  1. 获取指定 id 的 users
GET /api/users/100
  1. 新建一条 users 记录
POST /api/users
  1. 更新一条 users 记录
PUT /api/users/100
  1. 删除一条 users 记录
DELETE /api/users/100
  1. 获取一个 users 的所有消费账单
GET  /api/users/100/bill
  1. 获取一个 user 指定时间的消费账单
GET  /api/users/100/bill?from=201910&to=201911

以上其中 RESTful 风格 API 几乎包含常见业务情况。

三、Nodejs 实现 RESTful API

1. 初始化 mock 数据

本案例使用 mock 数据来演示,如下:

{
   "user1" : {
      "name" : "leo",
      "password" : "123456",
      "profession" : "teacher",
      "id": 1
   },
   "user2" : {
      "name" : "pingan8787",
      "password" : "654321",
      "profession" : "librarian",
      "id": 2
   },
   "user3" : {
      "name" : "robin",
      "password" : "888888",
      "profession" : "clerk",
      "id": 3
   }
}

我们将实现以下 RESTful API :

2. 获取用户列表

这一步我们会创建 RESTful API 中的 /users,使用 GET 来读取用户的信息列表

// index.js
const express = require('express');
const app = express();
const fs = require("fs");
// 定义 读取用户的信息列表 的接口
app.get('/users', (req, res) => {
   fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => {
       console.log( data );
       res.end( data );
   });
})
const server = app.listen(8081, function () {
    const {address, port} = server.address();
    console.log("server run in: http://%s:%s", address, port);
})

3. 添加用户

这一步我们会创建 RESTful API 中的 /users,使用 POST 来添加用户记录

// index.js
// 省略之前文件 只展示需要实现的接口
// mock 一条要新增的数据
const user = {
   "user4" : {
      "name" : "pingan",
      "password" : "password4",
      "profession" : "teacher",
      "id": 4
   }
}
// 定义 添加用户记录 的接口
app.post('/users', (req, res) => {
   // 读取已存在的数据
   fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => {
       data = JSON.parse( data );
       data["user4"] = user["user4"];
       console.log( data );
       res.end( JSON.stringify(data));
   });
})

4. 获取用户详情

这一步我们在 RESTful API 中的 URI 后面加上 /users/:id,使用 GET 来获取指定用户详情

// index.js
// 省略之前文件 只展示需要实现的接口
// 定义 获取指定用户详情 的接口
app.get('/users/:id', (req, res) => {
   // 首先我们读取已存在的用户
   fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => {
       data = JSON.parse( data );
       const user = data["user" + req.params.id] 
       console.log( user );
       res.end( JSON.stringify(user));
   });
})

5. 删除指定用户

这一步我们会创建 RESTful API 中的 /users,使用 DELETE 来删除指定用户

// index.js
// 省略之前文件 只展示需要实现的接口
// mock 一条要删除的用户id
const id = 2;
app.delete('/users', (req, res) => {
   fs.readFile( __dirname + "/" + "users.json", 'utf8', (err, data) => {
       data = JSON.parse( data );
       delete data["user" + id];
       console.log( data );
       res.end( JSON.stringify(data));
   });
})

四、REST 最佳实践

1. URL 设计

1.1 "动词 + 宾语"的操作指令结构

客户端发出的数据操作指令都是"动词 + 宾语"的结构。

如上面提到的,GET /user 这个命令,GET 是动词,/user 是宾语。根据 HTTP 规范,动词一律大写。

动词通常有以下五种 HTTP 方法:

GET:读取(Read) POST:新建(Create) PUT:更新(Update) PATCH:更新(Update),通常是部分更新 DELETE:删除(Delete)

1.2 宾语必须是名词

宾语就是 API 的 URL,是 HTTP 动词作用的对象。它应该是名词,不能是动词。

比如,/users 是正确的,因为 URL 是名词,而下面就都是错误的了:

/getUsers
/createUsers
/deleteUsers

1.3 建议复数 URL

因为 URL 是名词,没有单复数的限制,但是还是建议如果是一个集合,就使用复数形式。如 GET /users 来读取所有用户列表。

1.4 避免多级 URL

避免在多层级资源时,使用多级 URL。常见案例如获取某位用户的购买过的某一类商品:

GET /users/100/product/120

这种 URL 语意不明,也不利拓展,建议只有第一级,其他级别用查询字符串来表达:

GET /users/100?product=120

2. 准确的状态码表示

HTTP 五大类状态码有100多种,每一种状态码都有标准的(或者约定的)解释,客户端只需查看状态码,就可以判断出发生了什么情况,所以服务器应该返回尽可能精确的状态码。

这边列举几个经常使用的状态码介绍:

  • 303 See Other:表示参考另一个 URL。
  • 400 Bad Request:服务器不理解客户端的请求,未做任何处理。
  • 401 Unauthorized:用户未提供身份验证凭据,或者没有通过身份验证。
  • 403 Forbidden:用户通过了身份验证,但是不具有访问资源所需的权限。
  • 404 Not Found:所请求的资源不存在,或不可用。
  • 405 Method Not Allowed:用户已经通过身份验证,但是所用的 HTTP 方法不在他的权限之内。
  • 410 Gone:所请求的资源已从这个地址转移,不再可用。
  • 415 Unsupported Media Type:客户端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是客户端要求返回 XML 格式。
  • 422 Unprocessable Entity:客户端上传的附件无法处理,导致请求失败。
  • 429 Too Many Requests:客户端的请求次数超过限额。
  • 500 Internal Server Error:客户端请求有效,服务器处理时发生了意外。
  • 503 Service Unavailable:服务器无法处理请求,一般用于网站维护状态。

3. 服务端响应

3.1 应该返回 JSON 对象

API 返回的数据格式应该是 JSON 一个对象。

3.2 发生错误时,不要返回 200 状态码

在发生错误时,如果还返回 200 状态码,前端需要解析返回数据才知道错误信息,这样实际上取消了状态码,是不恰当的。

正确的做法应该是在错误时,返回对应错误状态码,并将错误信息返回:

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
  "error": "Invalid payoad.",
  "detail": {
     "surname": "This field is required."
  }
}

参考资料

  1. 《维基百科 - 表现层状态转换》
  2. 《RESTful风格的springMVC》
  3. 《Node.js RESTful API》
  4. 《RESTful API 最佳实践》


目录
相关文章
|
15天前
|
API 持续交付 开发者
后端开发中的微服务架构实践与挑战
在数字化时代,后端服务的构建和管理变得日益复杂。本文将深入探讨微服务架构在后端开发中的应用,分析其在提高系统可扩展性、灵活性和可维护性方面的优势,同时讨论实施微服务时面临的挑战,如服务拆分、数据一致性和部署复杂性等。通过实际案例分析,本文旨在为开发者提供微服务架构的实用见解和解决策略。
|
16天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####
|
4天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
24 5
|
7天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
5天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####
|
5天前
|
消息中间件 缓存 Cloud Native
云原生架构下的性能优化实践与挑战####
随着企业数字化转型的加速,云原生架构以其高度解耦、弹性伸缩和快速迭代的特性,成为现代软件开发的首选模式。本文深入探讨了云原生环境下性能优化的关键策略与面临的主要挑战,通过案例分析,揭示了如何有效利用容器化、微服务、动态调度等技术手段提升应用性能,同时指出了在复杂云环境中确保系统稳定性和高效性的难题,为开发者和架构师提供了实战指南。 ####
18 3
|
6天前
|
运维 Kubernetes Cloud Native
深入理解云原生架构:从理论到实践
【10月更文挑战第38天】本文将引导读者深入探索云原生技术的核心概念,以及如何将这些概念应用于实际的软件开发和运维中。我们将从云原生的基本定义出发,逐步展开其背后的设计哲学、关键技术组件,并以一个具体的代码示例来演示云原生应用的构建过程。无论你是云原生技术的初学者,还是希望深化理解的开发者,这篇文章都将为你提供有价值的见解和实操指南。
|
5天前
|
Kubernetes Cloud Native 持续交付
云原生技术在现代应用架构中的实践与思考
【10月更文挑战第38天】随着云计算的不断成熟和演进,云原生(Cloud-Native)已成为推动企业数字化转型的重要力量。本文从云原生的基本概念出发,深入探讨了其在现代应用架构中的实际应用,并结合代码示例,展示了云原生技术如何优化资源管理、提升系统弹性和加速开发流程。通过分析云原生的优势与面临的挑战,本文旨在为读者提供一份云原生转型的指南和启示。
19 3
|
5天前
|
运维 Kubernetes Cloud Native
云原生技术在现代应用架构中的实践与挑战####
本文深入探讨了云原生技术的核心概念、关键技术组件及其在实际项目中的应用案例,分析了企业在向云原生转型过程中面临的主要挑战及应对策略。不同于传统摘要的概述性质,本摘要强调通过具体实例揭示云原生技术如何促进应用的灵活性、可扩展性和高效运维,同时指出实践中需注意的技术债务、安全合规等问题,为读者提供一幅云原生技术实践的全景视图。 ####
|
8天前
|
监控 API 持续交付
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在后端开发中的应用,分析了其优势、面临的挑战以及最佳实践策略。不同于传统的单体应用,微服务通过细粒度的服务划分促进了系统的可维护性、可扩展性和敏捷性。文章首先概述了微服务的核心概念及其与传统架构的区别,随后详细阐述了构建微服务时需考虑的关键技术要素,如服务发现、API网关、容器化部署及持续集成/持续部署(CI/CD)流程。此外,还讨论了微服务实施过程中常见的问题,如服务间通信复杂度增加、数据一致性保障等,并提供了相应的解决方案和优化建议。总之,本文旨在为开发者提供一份关于如何在现代后端系统中有效采用和优化微服务架构的实用指南。 ####