从 Flask 到 Gin —— 装饰器和中间件
文章目录
本文是 从 Flask 到 Gin 系列的第 5 篇。
Python 提供了一个有趣且有用的语法糖:装饰器。使用装饰器,我使用装饰器在 Flask 中实现了鉴权:用户进行数据提交的时候,需要提供一个 TOKEN,这个 TOKEN 如果解密成功,就正常进行 response 相关的逻辑,反之则直接返回 403 错误。
鉴权逻辑就是使用 Python 的装饰器实现的。但 Golang 没有装饰器特性,我会使用 Gin 的中间件机制来代替它。
实现 mjst_checker
装饰器
mjst_checker
是一个装饰器,经过它装饰的路由方法,会自动处理请求中带来的名为 mjst 的 token,token 中包含用户的权限信息,还有 token 有效期等等信息,如果信息正常,那么路由会继续正常处理,否则会得到 403 错误。
来看看 mjst_checker
装饰器的定义:
1from functools import wraps
2
3class PYConf(dict):
4 """基于 Python dict 的配置文件。
5
6 dict 默认不适合当作配置文件对象使用。如要有下面几点不便:
7
8 #. 对于不存在的 key,会 raise KeyError 错误;
9 #. dict不能使用 ``.`` 语法访问。
10
11 :class:`PYConf` 解决了这些问题,还另外提供了一些方法在使用上更加方便。
12
13 """
14
15 def __missing__(self, key):
16 return None
17
18 def __getattr__(self, name):
19 return self[name]
20
21 def __setattr__(self, name, value):
22 self[name] = value
23
24 def __delattr__(self, name):
25 del self[name]
26
27
28def mjst_checker(*typeids):
29 """ MJST 的检测器,检测 HEADER 中传来的 MJST 是否正常
30 :param typeids: typeid 数组
31 """
32
33 def decorator(f):
34 @wraps(f)
35 def decorated_fun(*args, **kwargs):
36 # _get_mjst 从请求中获取 mjst 的值
37 mjst = _get_mjst()
38 # 将要加入 kwargs 中的mjst解析后的值,默认是一个错误,代表没有解析成功
39 mjstarg = PYConf({'error': True, 'code': 403, 'message': 'MJST DECODE ERROR!'})
40 try:
41 # _decode_mjst 为具体的解密方法
42 mjstobj = _decode_mjst(typeids, mjst)
43 if mjstobj is not None:
44 mjstarg = mjstobj
45 except Exception as e:
46 logger.error('@mjst_checker decode_mjst:%s error:%s', mjst, str(e))
47 # 此处的 mjstarg 一定为非 None,至少包含上面提供的 403 错误。这里不使用 HTTP 403,让客户端知道出了什么错
48 kwargs['mjst'] = mjstarg
49 return f(*args, **kwargs)
50
51 return decorated_fun
52
53 return decorator
其中涉及的 _get_mjst
和 _decode_mjst
方法的作用已经在注释中解释过了,具体实现就不提供了。让我们看看 mjst_checker
这个装饰器应该怎么使用。
mjst_checker
的用法
在 从 Flask 到 Gin —— SQLAlchemy 和 gorm 中我们定义了两个路由方法,register
和 active
是没有增加鉴权功能的,现在可以补全了:
1@audible.route('/register/', methods=['GET'])
2@mjst_checker(51, 55)
3def register(mjst):
4 """ 获取注册数据
5 """
6 if mjst.error:
7 return responseto(data=mjst)
8 return _response_register_or_active(LogRegister)
9
10
11@audible.route('/active/', methods=['GET'])
12@mjst_checker(51, 55)
13def active(mjst):
14 """ 获取活跃数据
15 """
16 if mjst.error:
17 return responseto(data=mjst)
18 return _response_register_or_active(LogActive)
如果 mjst 解析失败,得到的响应是一个 http code 200,但返回的 json 中的 code 值为 403。responseto
的具体定义见 从 Flask 到 Gin —— 处理 JSON。
实现 MjstChecker
中间件
Gin 为我们提供了中间件支持,让我们可以在请求收到以及响应之间做许多事。看看 MjstChecker
这个中间件的定义:
1package middlewares
2
3import (
4 "net/http"
5 "github.com/gin-gonic/gin"
6)
7// MjstChecker 检查 mjst 的权限是否匹配
8// typeids 支持的 usertype
9func MjstChecker(typeids []int) gin.HandlerFunc {
10 return func(c *gin.Context) {
11 // getMjst 从请求中获取 mjst 的值
12 mjstObj := getMjst(typeids, c)
13 if mjstObj == nil {
14 c.AbortWithStatusJSON(http.StatusOK, gin.H{
15 "error": true,
16 "code": 403,
17 "message": "MJST DECODE ERROR!",
18 })
19 return
20 }
21 c.Set("mjst", mjstObj)
22 c.Next()
23 }
24}
getMjst
在注释中已经说明了用途,也就不提供具体定义了。和 Python 版本不同的是,这里把获取 mjst 请求参数以及解密 token 写到了一个方法中。如果解析失败的话,会直接返回一个 code 403 的 JSON 响应。
MjstChecker
的用法
在 从 Flask 到 Gin —— SQLAlchemy 和 gorm 中,我们定义了初始化路由的方法 InitAudible
。现在进行一点点修改,将 MjstChecker
这个中间件串联到路由方法前面,增加鉴权功能:
1package routers
2
3import (
4 "mjp/middlewares"
5 "github.com/gin-gonic/gin"
6)
7// InitAudible register the audible api
8func InitAudible(router *gin.RouterGroup) {
9 router.GET("/register", middlewares.MjstChecker([]int{51, 55}), AudibleRegister)
10 router.GET("/active", middlewares.MjstChecker([]int{51, 55}), AudibleActive)
11}
如果鉴权失败,客户端收到的结果为:
1{
2 "code": 403,
3 "error": true,
4 "message": "MJST DECODE ERROR!"
5}
参考
- 从 Flask 到 Gin —— 处理 JSON
- 从 Flask 到 Gin —— SQLAlchemy 和 gorm
- PEP 318 -- Decorators for Functions and Methods
- Custom Middleware
- Go Web轻量级框架Gin学习系列:中间件使用详解
阅读系列所有文章:从 Flask 到 Gin。
- 文章ID:2686
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/flask-to-gin-decorator-and-middleware/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。