对前后端分离设计模式的理解总结(部分Django描述)

最近对前后端分离设计模式的理解总结:

为什么要做前后端分离:

  • 有些人会说:因为职责明确,因为不再用模板做视图层 render HTML,后端不用自己写前端就会轻松些... 这些大多说的不错,但还是比较表象,主要的原因其实是 源于需求变化

    在现代web开发中,我们对于前端的需求越来越复杂,我们需要 PC端浏览器、手机端浏览器 、Android APP 和 iPhone APP 、微信小程序等多种需求。

    由于我们前端可能涉及设计到的页面种类越来越多,而 vue、angular、react 等前端框架越来越好的支持:“ Code once,run everywhere. ”,所以前端的开发渐渐独立、并更为体系化。

    故而曾经那种单凭一套模板渲染页面的方法似乎太局限了。

  • 而一旦采用 前后端分离 的模式,并且采用 restful 设计规范,那么后端就只需要提供数据 API 接口即可,后端只需要长期维护这一套代码就行了。

Django 的 FBV & CBV

FBV:Function base view 基于方法的视图

CBV:Class base view 基于类的视图

在我最开始尝试用 Flask 框架遵循 Restful 规范按路由设计接口时,常常有这样几个疑问和思考:

  • 我的需求这么多,难道没一个都要设计一个接口吗?这样方便维护吗?
  • 比如一个接口可能有 GET、POST 或是 PUT 等类型的请求,我难道每次都要写一个选择分支:if request.method == ' ... ': 才可以吗?

所以之后我了解到,其实是我没有了解 FBV 与 CBV 的概念。

正所谓:类就是 把数据封装进对象里 ,并赋予对象 行为 的能力。

所以我们完全可以把一个需求的接口封装成为一个类:

from django.views import View

class UserView(View): # 我们假设这是一个关于用户的接口,django/Flask中路由也叫视图。
        
    def get(self, request, *args, **kwargs):
        # ... 对应GET请求
        
    def post(self, request, *args, **kwargs):
        # ... 对应POST请求
        
    def put(self, request, *args, **kwargs):
        # ... 对应PUT请求
    
    def delete(self, request, *args, **kwargs):
        # ... 对应DELETE请求
        

因为继承了 django 的 View 类,所以在默认情况下,会自动根据请求类型映射该类中对应的请求方法。

但是在所有的 python web 框架乃至一些其他语言的框架之中,对 HTTP 请求类型的方法映射都是由一个专门的反射函数来实现的

# 本身在django的源码 base.py 中:
def dispatch(self, request, *args, **kwargs):
    # 是有这样一个基础的反射方法的。
    
# Override (但在我们继承了 View 的类中,我们可以重载它试验一下)
from django.views import View
class User(View):
    def dispatch(self, request, *args, **kwargs):
        func = getattr(self, request.method.lower()) #必须要小写化不然映射不到我们刚才写的四个对应方法
        ret = func(self, request, *args, **kwargs)
        return ret

    # ...下面四个方法同上

所以,总结如下:

  • CBV:基于反射实现根据请求方式不同,执行不同的方法。

    原理:URL -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)

另外值得一提的是:自己那个类中的 dispatch 方法中如果不自己去映射而是调用父类(django 的 View)的 dispatch 方法,另外还在前后做一些附加操作,这样的功能跟 “装饰器” 就很相似了。

RESTful API 设计规范

RESTful 规范是一种建议而非硬性要求,但是在前后端分离是大趋势的当下,越来越多的程序员们开始喜欢遵照 RESTful 规范来设计后端接口。

一共有10个项目,那让我们一起来慢慢学习吧!

  1. API与用户的通信协议,总是使用HTTPS协议

    • 因为HTTPS比HTTP更加安全,所以在建立、部署大中型网站时企业都会选择使用HTTPS,但是由于HTTPS需要配置专门的证书,而如果需要具备较高的认可度的证书,则需要从证书商处购买,而价格大多不便宜。
  2. 域名配置要体现自己是个 API

    • 子域名形式:https://api.example.com ( 存在跨域的问题 )

      引用他人的 api接口 也得在自己的请求处解决跨域问题

    • 简单式:https://www.example.com/api/

  3. 后端API接口请区分版本

    • 一般都是加在 URL 上,其他的一般业内很少用:

      http://api.example.com/v1

  4. 路径 -> 面向资源编程:

    何为面向资源编程呢?就是把互联网上的任何东西都视为一种资源,而我们在后端对该实体描述的 API 路由都为名词。

    而不是类似于 getUser、addUser 这样的动词!

    具体请看下一条 ...

  5. methods :HTTP方法

    始终记住 RESTful 设计要充分利用 HTTP协议里的 GET、POST、PUT、PATCH 和 DELETE 等 方法标志 来表达对数据的 增删改查

    • GET /collection:返回资源对象的列表(数组)
    • GET /collection/resource:返回单个资源对象
    • POST /collection:返回新生成的资源对象
    • PUT /collection/resource:返回完整的资源对象
    • PATCH /collection/resource:返回完整的资源对象
    • DELETE /collection/resource:返回一个空文档
  1. 对资源的条件过滤 -> 用 URL传参 来指定过滤条件

    类似于 例如查订单时 http://api.mall.com/orders?sortby=dectime 则是以按降序时间排列订单...

  2. 一定要使用 状态码

    常见的状态码:

    200 系列: 成功及其附属状态信息

    • 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    • 204 NO CONTENT - [DELETE]:用户删除数据成功。
    • ...

300 系列:重定向类

  • 例如 301 MOVED PERMANENTLY
  • ...

400 系列 :客户端错误

  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocessable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
  • ...

500 系列: 服务端错误

  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
  • ...

单光用 HTTP状态码 在实际开发中是远远不够的哦!

一般还会加上一个我们自己的 code;

参见Alibaba Alipay支付宝文档:https://docs.open.alipay.com/common/105806 code( 返回码 )

这些自定义的返回码大大增加了我们能够表示的 服务对客户端请求的响应状态的类型。

由于状态码十分有限,所以一般前端大多会被要求处理 自定义的这个code。

  1. 错误处理

    承接上一个小段,当状态码是 4xx 时,应当返回错误信息,error 当做 key:

    {
        error:"Invalid API key!"
    }
    
  2. 以 API url 来确定返回类型!

    • GET /collection:返回资源对象的列表(数组)
    • GET /collection/resource:返回单个资源对象
    • POST /collection:返回新生成的资源对象
    • PUT /collection/resource:返回完整的资源对象
    • PATCH /collection/resource:返回完整的资源对象
    • DELETE /collection/resource:返回一个空文档

    这样干巴巴地说自然很难理解那个 collection 是什么意思:还是以订单举例:

    http://api.mall.com/orders/18924442 => orders 是复数(一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。),我的操作目标是 订单集合,而后面的 18924442(一个订单 id 示例)就代表着资源对象的实体 resource!

    那么我对这个 api url 的 post 、delete 、put / patch 和 get 就分别对应了该资源的 增删改查 接口。

  3. HypermediaAPI:

    即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

    比如:对于我正准备要实现的 RPZ计协 · 科丁特沃夫咖啡厅论坛 桌帖的评论列表 这件事情:

    • /coffeeComments?postid=12 第十二个咖啡桌帖的评论列表:
      [
          {
              id: '100030001',
              author: '飞翔的海猫',
              content: '你这个写的有bug呀!'
              datetime: '2019-01-19 22:10:35',
              likes: 0
              commentsList: 'https://api.sicnurpz.online/coffeeComments?commentid=100030001'
          }
      ]
      
    • 前面的都是 评论数据类型该有的基本信息比如 评论者的昵称、评论内容等,但为什么我要在最后加上一个 commentsList,还是个 url 地址字符串呢?

      因为这样我就不用再在服务端去拼接 查询该条评论的评论列表 的字符串了! 提高了后端接口处理的效率,在现代服务器存储量充足的情况下,数据存储空间 换 网络响应时间自然是不亏的啦!

你可能感兴趣的:(对前后端分离设计模式的理解总结(部分Django描述))