Python从入门到精通day52
RESTful架构和DRF入门将软件Software、平台Platform、基础设施Infrastructure封装为服务Service是当下多数 IT 企业的核心实践方向这也是大家熟知的 SaaS软件即服务、PaaS平台即服务和 IaaS基础设施即服务的核心逻辑。面向服务架构SOA的实现方式多种多样RPC远程过程调用、Web Service、REST 等均是主流方案从技术维度看SOA 是一种抽象化、松耦合的粗粒度软件架构而从业务维度来讲其核心是 “复用” 与 “互操作”—— 将系统资源整合为标准化、可调用的服务实现资源的灵活重组与复用。在众多 SOA 实现方案中REST 因适配互联网应用的特性成为首选符合 REST 规范的架构也被称为 RESTful 架构。REST概述REST 这一概念由 Roy Thomas Fielding 在 2000 年的博士论文中提出他不仅是 HTTP 协议1.0 和 1.1 版本的核心设计者还是 Apache 服务器软件的主要开发者、Apache 基金会首任主席。在这篇论文中Roy 将互联网软件的架构原则命名为REST即 REpresentational State Transfer 的缩写中文常译作 “表现层状态转移”也译作 “表述状态转移”。这里的 “表现层”本质是 “资源” 的表现形式。所谓 “资源”指的是网络中的一个实体或具体信息比如一段文本、一张图片、一首音频甚至是一项服务。每个资源都可以通过 URI统一资源定位符唯一标识访问该 URI 即可获取对应的资源。同一资源可呈现多种形式这种具体的呈现方式就是 “表现层”例如文本可采用text/plain、text/html、text/xml、application/json等格式图片可采用image/jpeg、image/png等格式。需要注意的是URI 仅标识资源本身而非其表现形式。严格来说网址末尾的.html后缀并无必要 —— 该后缀属于 “表现层” 范畴而 URI 应仅指向 “资源” 的位置资源的具体表现形式应通过 HTTP 请求头中的Accept和Content-Type字段指定这两个字段才是对 “表现层” 的精准描述。客户端与服务器的交互过程本质是数据和状态的变更过程。Web 应用通常基于 HTTP 协议通信客户端通过 HTTP 请求触发服务器的 “状态转移”而这种转移基于资源的表现层完成因此称为 “表现层状态转移”。客户端可通过 HTTP 动词GET、POST、PUT/PATCH、DELETE实现对资源的四类核心操作• GET获取资源• POST新建资源也可用于更新• PUT/PATCH更新资源PUT 需提供完整信息PATCH 仅需部分• DELETE删除资源简言之RESTful 架构的核心是“每个 URI 对应一种资源客户端通过四种 HTTP 动词操作服务器端资源实现资源的表现层状态转移”。设计 RESTful 架构的第一步是采用符合 REST 风格的 URI 对外提供资源但真正的 RESTful 架构还需满足 “无状态” 和 “幂等性” 两大核心特性后续会详细讲解。以下是 REST 风格 URI 的示例供设计参考请求方法HTTP动词URI解释GET/students/获取所有学生POST/students/新建一个学生GET/students/ID/获取指定ID的学生信息PUT/students/ID/更新指定ID的学生信息提供该学生的全部信息PATCH/students/ID/更新指定ID的学生信息提供该学生的部分信息DELETE/students/ID/删除指定ID的学生信息GET/students/ID/friends/列出指定ID的学生的所有朋友DELETE/students/ID/friends/ID/删除指定ID的学生的指定ID的朋友DRF使用入门在 Django 项目中实现 REST 架构对外提供 REST 风格的 API 接口最常用的第三方库是djangorestframework简称 DRF。1.DRF 的安装与配置安装DRF执行以下命令完成 DRF 的安装pip install djangorestframework配置DRF在 Django 项目的settings.py文件中将 DRF 添加到 INSTALLED_APPS并根据业务需求配置相关参数INSTALLED_APPS [ rest_framework, ]# 下面的配置根据项目需要进行设置REST_FRAMEWORK { # 配置默认页面大小 # PAGE_SIZE: 10, # 配置默认的分页类 # DEFAULT_PAGINATION_CLASS: ..., # 配置异常处理器 # EXCEPTION_HANDLER: ..., # 配置默认解析器 # DEFAULT_PARSER_CLASSES: ( # rest_framework.parsers.JSONParser, # rest_framework.parsers.FormParser, # rest_framework.parsers.MultiPartParser, # ), # 配置默认限流类 # DEFAULT_THROTTLE_CLASSES: ( # ... # ), # 配置默认授权类 # DEFAULT_PERMISSION_CLASSES: ( # ..., # ), # 配置默认认证类 # DEFAULT_AUTHENTICATION_CLASSES: ( # ..., # ),}2.序列化器编写前后端分离开发模式下后端需向前端 / 移动端提供 JSON 格式的 API 数据这就需要对模型对象进行序列化处理。DRF 封装了Serializer和ModelSerializer类通过继承这两个类可自定义序列化器将模型对象转换为字典格式。示例代码如下列化器用于将对象处理成字典代码如下所示。from rest_framework import serializers class SubjectSerializer(serializers.ModelSerializer): class Meta: model Subject fields __all__上面的代码直接继承了ModelSerializer通过Meta类的model属性指定要序列化的模型以及fields属性指定需要序列化的模型字段稍后我们就可以在视图函数中使用该类来实现对Subject模型的序列化。3. 视图函数开发DRF 支持两种接口实现方式FBV基于函数的视图和 CBV基于类的视图。先以 FBV 为例实现数据接口from rest_framework.decorators import api_viewfrom rest_framework.response import Responseapi_view((GET, ))def show_subjects(request: HttpRequest) - HttpResponse: subjects Subject.objects.all().order_by(no) # 创建序列化器对象并指定要序列化的模型 serializer SubjectSerializer(subjects, manyTrue) # 通过序列化器的data属性获得模型对应的字典并通过创建Response对象返回JSON格式的数据 return Response(serializer.data)相较于传统的bpmapper序列化方式DRF 的代码更简洁且自带可视化接口调试页面便于接口测试与调试。直接使用上一节写好的页面就可以通过Vue.js把上面接口提供的学科数据渲染并展示出来此处不再进行赘述。4. 老师信息接口实现步骤 1编写序列化器class SubjectSimpleSerializer(serializers.ModelSerializer): class Meta: model Subject fields (no, name)class TeacherSerializer(serializers.ModelSerializer): class Meta: model Teacher exclude (subject, )步骤 2编写视图函数api_view((GET, ))def show_teachers(request: HttpRequest) - HttpResponse: try: sno int(request.GET.get(sno)) subject Subject.objects.only(name).get(nosno) teachers Teacher.objects.filter(subjectsubject).defer(subject).order_by(no) subject_seri SubjectSimpleSerializer(subject) teacher_seri TeacherSerializer(teachers, manyTrue) return Response({subject: subject_seri.data, teachers: teacher_seri.data}) except (TypeError, ValueError, Subject.DoesNotExist): return Response(status404)步骤 3配置 URL 映射在项目的 urls.py 中添加接口路由urlpatterns [ path(api/teachers/, show_teachers), ]步骤 4Vue.js 前端渲染以下是基于 Vue.js 的前端页面示例用于渲染老师信息接口返回的数据!DOCTYPE htmlhtml langenhead meta charsetUTF-8 title老师信息/title style /* 此处省略掉层叠样式表 */ /style/headbody div idcontainer h1{{ subject.name }}学科的老师信息/h1 hr h2 v-ifloaded teachers.length 0暂无该学科老师信息/h2 div classteacher v-forteacher in teachers div classphoto img :src/static/images/ teacher.photo height140 alt /div div classinfo div spanstrong姓名{{ teacher.name }}/strong/span span性别{{ teacher.sex | maleOrFemale }}/span span出生日期{{ teacher.birth }}/span /div div classintro{{ teacher.intro }}/div div classcomment a href click.preventvote(teacher, true)好评/anbsp;nbsp; (strong{{ teacher.good_count }}/strong) nbsp;nbsp;nbsp;nbsp; a href click.preventvote(teacher, false)差评/anbsp;nbsp; (strong{{ teacher.bad_count }}/strong) /div /div /div a href/static/html/subjects.html返回首页/a /div script srchttps://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js/script script let app newVue({ el: #container, data: { subject: {}, teachers: [], loaded: false }, created() { fetch(/api/teachers/ location.search) .then(resp resp.json()) .then(json { this.subject json.subject this.teachers json.teachers }) }, filters: { maleOrFemale(sex) { return sex? 男: 女 } }, methods: { vote(teacher, flag) { let url flag? /praise/ : /criticize/ url ?tno teacher.no fetch(url).then(resp resp.json()).then(json { if (json.code 10000) { if (flag) { teacher.good_count json.count } else { teacher.bad_count json.count } } }) } } }) /script/body/html前后端分离下的用户登录HTTP 协议本身是无状态的 —— 一次请求完成后连接断开服务器无法识别后续请求的用户身份。但 Web 应用需要会话管理来跟踪用户状态从而实现权限控制和个性化服务。传统的会话管理方式是基于 Session 实现用户登录成功后服务器创建 Session 对象存储用户信息将 Session ID 写入浏览器 Cookie后续请求时服务器通过 Cookie 中的 Session ID 找到对应的 Session 对象获取用户数据。但这种方式与 REST 架构的 “无状态” 特性冲突 —— 服务器存储 Session 会增加水平扩展的难度新增服务器节点需同步 Session 数据。解决该问题的方案主要有两种1.引入 Redis 等缓存服务器集中存储 Session实现多节点共享2. 放弃 Session采用基于 Token 的用户跟踪方案。基于 Token 的用户跟踪无需服务器存储用户状态更适配 RESTful 架构的无状态特性核心流程如下1. 登录验证用户登录成功后服务器生成包含用户标识、过期时间等信息的加密 Token需生成签名防篡改并返回给前端2. Token 存储前端将 Token 保存到浏览器本地存储localStorage/sessionStorage/CookieVue 项目也可通过 Vuex 管理3. 路由守卫前端路由跳转时校验本地存储中的 Token无 Token 则跳转至登录页4. 请求携带 Token前端每次请求 API 时在 HTTP 请求头中携带 Token后端校验 Token 的有效性无 Token/Token 无效 / 过期则返回 401 状态码5. 401 处理前端收到 401 响应时自动重定向到登录页面。生成 Token 的主流方案是 JSON Web TokenJWT下面详细讲解 JWT 的使用。JWT 核心原理JSON Web TokenJWT是基于 RFC 7519 标准的令牌规范是 RESTful 架构下用户认证的主流方案。JWT 由三部分组成各部分通过.分隔分别是头部Header、载荷Payload和签名Signature。1. 头部Header头部用于声明令牌类型和签名算法示例如下{ alg: HS256, typ: JWT}•alg指定签名算法默认 HMAC SHA256简写为 HS256•typ令牌类型固定为 JWT。2. 载荷Payload载荷用于存储实际传递的业务数据包含官方定义的7个可选字段和自定义字段:官方标准字段• iss 签发人• exp过期时间• sub主题• aud受众• nbf生效时间• iat签发时间• jti编号自定义字段示例JSON{ sub: 1234567890, nickname: jackfrued, role: admin}3. 签名Signature签名用于验证令牌的完整性防止篡改。生成签名需满足以下条件• 服务器持有唯一的密钥Secret Key不可泄露• 采用 Header 中指定的算法对 Base64 编码后的 Header 和 Payload 进行加密公式HS256(base64Encode(header) . base64Encode(payload), secret)最终将 Header、Payload、Signature 三部分用.拼接即生成完整的 JWT 令牌。JWT的优缺点优点1. 易扩展Token 存储在前端服务器无需维护状态可轻松实现水平扩展2. 防 CSRFToken 需通过 JavaScript 主动添加到请求头而非浏览器自动携带能有效防范 CSRF 攻击3. 防篡改签名机制确保 Token 被篡改后无法通过验证提升安全性。缺点4. 易受 XSS 攻击恶意脚本可通过 XSS 漏洞窃取本地存储的 Token5. 无法主动作废Token 在过期前无法手动失效需额外设计令牌黑名单机制6. 泄露风险Token 是用户身份凭证一旦泄露攻击者可获取用户所有权限。建议缩短 Token 有效期并对高权限操作增加二次验证如短信验证码。PyJWT 实战使用Python 中可通过 PyJWT 库实现 JWT 的生成与验证步骤如下1. 安装 PyJWTpip install pyjwt2. 生成 JWT 令牌payload { exp: datetime.datetime.utcnow() datetime.timedelta(days1), userid: 10001}token jwt.encode(payload, settings.SECRET_KEY).decode()3. 验证 JWT 令牌try: token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ4NzIzOTEsInVzZXJpZCI6MTAwMDF9.FM-bNxemWLqQQBIsRVvc4gq71y42I9m2zt5nlFxNHUo payload jwt.decode(token, settings.SECRET_KEY)except InvalidTokenError: raise AuthenticationFailed(无效的令牌或令牌已经过期)如果不清楚JWT具体的使用方式可参考之前的内容里面提供了完整的投票项目代码的地址。神器,助力学习,工作国内直接使用顶级AI工具谷歌浏览器访问https://www.nezhasoft.cloud/r/vMPJZr