引入:
近期参与的项目是一个14年基于Django开发的web系统,因为项目早期的同事并未考虑前后端分离也未遵循标准的restful接口设计,现在在逐渐拆分微服务的过程深深感到遵从标准restful设计的重要性。在这里和大家分享一下自己的拙见:
拿到手的项目全部基于FBV(function base views)设计,这样的设计导致的情况就是会有一堆url,一个url路由映射一个function,一个function完成一个功能,比如一下例子:
1
# urls.py
2
3
urlpatterns = [
4
url(r'^create_book/$', views.create_book),
5
url(r'^update_book/$', views.update_book),
6
url(r'^get_book/$', views.get_book),
7
url(r'^delete_book/$', views.delete_book),
8
9
url(r'^create_author/$', views.create_author),
10
url(r'^update_author/$', views.update_book),
11
url(r'^get_author/$', views.get_author),
12
url(r'^delete_author/$', views.delete_author),
13
14
]
1
# views.py
2
import json
3
from django.shortcuts import HttpResponse
4
5
6
def create_book(request):
7
return HttpResponse(json.dumps({'code': '20000'}))
8
9
10
def update_book(request):
11
# do something
12
return HttpResponse(json.dumps({'code': '20000'}))
13
14
15
def get_book(request):
16
# do something
17
return HttpResponse(json.dumps({'code': '20000'}))
18
19
20
def delete_book(request):
21
# do something
22
return HttpResponse(json.dumps({'code': '20000'}))
23
24
25
def create_author(request):
26
return HttpResponse(json.dumps({'code': '20000'}))
27
28
29
def update_author(request):
30
# do something
31
return HttpResponse(json.dumps({'code': '20000'}))
32
33
34
def get_author(request):
35
# do something
36
return HttpResponse(json.dumps({'code': '20000'}))
37
38
39
def delete_author(request):
40
# do something
41
return HttpResponse(json.dumps({'code': '20000'}))
其实单纯的从功能实现来看,这种写法完全没有问题,但是有人问题就在于接口过多,不容易维护,前端开发者会很头疼,要记下如此多的接口
那能不能讲url简化?当然可以,或者有的项目中会出现以下实现方式:
1
# urls.py
2
urlpatterns = [
3
url(r'^book/$', views.book),
4
url(r'^author/$', views.author),
5
]
1
# views.py
2
def book(request):
3
if request.method == "GET":
4
# get a book
5
return HttpResponse(json.dumps({'code': '20000'}))
6
elif request.method == "POST":
7
# create a book
8
return HttpResponse(json.dumps({'code': '20000'}))
9
elif request.method == "PUT":
10
# update a book
11
return HttpResponse(json.dumps({'code': '20000'}))
12
elif request.method == "DELETE":
13
# delete a book
14
return HttpResponse(json.dumps({'code': '20000'}))
15
else:
16
# method not be support
17
return HttpResponse(json.dumps({'code': '40010'}))
18
19
20
def author(request):
21
if request.method == "GET":
22
# get a author
23
return HttpResponse(json.dumps({'code': '20000'}))
24
elif request.method == "POST":
25
# create a author
26
return HttpResponse(json.dumps({'code': '20000'}))
27
elif request.method == "PUT":
28
# update a author
29
return HttpResponse(json.dumps({'code': '20000'}))
30
elif rrequest.method == "DELETE":
31
# delete a author
32
return HttpResponse(json.dumps({'code': '20000'}))
33
else:
34
# method not be support
35
return HttpResponse(json.dumps({'code': '40010'}))
这里可以看到url是已经简化了,所有的功能都在function中做了实现,通过requests.method选择对应的处理逻辑
通过postman测试一下:
GET:
POST:
PUT,DELETE和POST请求的结果是一样的,因为django的csrf验证阻止我们的请求,这里先不做csrf token验证,为了方便先将csrf token中间件注释掉:
1
MIDDLEWARE_CLASSES = (
2
'django.contrib.sessions.middleware.SessionMiddleware',
3
'django.middleware.common.CommonMiddleware',
4
#'django.middleware.csrf.CsrfViewMiddleware',
5
'django.contrib.auth.middleware.AuthenticationMiddleware',
6
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
7
'django.contrib.messages.middleware.MessageMiddleware',
8
'django.middleware.clickjacking.XFrameOptionsMiddleware',
9
'django.middleware.security.SecurityMiddleware',
10
)
接下来重新提交,就可以返回正确的response了。
好像是达到了简化ur的l目的,但是还是通过FBV方式实现的接口,function体的内容过多,逻辑分支很多,要对请求进行判断并且实现对应的逻辑分支。代码整体上并不优雅,但是这样的代码有问题吗?并没有,只是CBV提供了更好的方式来实现接口的定义。
我们知道FBV就是在视图里使用函数处理请求。其实CBV就是在视图里使用类处理请求。
如果我们要通过类的方式处理请求,那上面的function就应该用以下方式实现:
1
# views.py
2
from django.views.generic.base import View
3
class Book(View):
4
def get(self, request):
5
# get a book
6
return HttpResponse(json.dumps({'code': '20000'}))
7
8
def post(self, request):
9
# create a book
10
return HttpResponse(json.dumps({'code': '20000'}))
11
12
def put(self, request):
13
# update a book
14
return HttpResponse(json.dumps({'code': '20000'}))
15
16
def delete(self, request):
17
# delete a book
18
return HttpResponse(json.dumps({'code': '20000'}))
这里我们继承了View,并且在Book类中定义了get,post,put,delete方法,我们其实,当一个请求过来的时候会交给对应的方法进行处理,比如,我们有一个请求是GET请求,那就应该交给get方法处理,那这是怎么实现的呢?
我们知道在django中,一个请求的生命周期:
#1.wsgi,请求封装后交给web框架 (Flask、Django)#2.中间件,对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session -#3.路由匹配 根据浏览器发送的不同url去匹配不同的视图函数#4.视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm、templates => 渲染 -#5.中间件,对响应的数据进行处理。#6.wsgi,将响应的内容发送给浏览器。
FBV和CBV在前两步是没有什么区别的,重点就在于第三步路由匹配:
FBV中路由匹配
url(r'^book/$', views.book),看上去很容易理解,将url是/book/的请求交给views.py中的book函数处理
CBV中路由匹配
url(r'^book/$', Book.as_view(), name='book'), Book是我们在views.py中定义的类,继承于View,那as_view()是从哪来的?我们没有在book中定义,而是父类(View)中定义的.
CBV是怎么将method交给对应方法处理的呢?来看源码:
as_view是一个闭包函数,返回值是内部view,我们只需要关心view部分,这里的request就是之前在FBV中使用的request,前几行就是将参数封装到类属性,重点在于self.dispatch,所有到CBV的请求都会以dispatch为入口,这里的self就是我们上面定义的Book类,
先会在Book类中找dispatch方法,我们没有定义,所以向上找,就用Book类的父类APIView类的despatch方法,接下来我们看APIView类的dispatch都做了什么:
这里的self.initialize_request做了什呢?注意看参数,(request, *args, **kwargs)这里的request是原本FBV中的第一个参数,那这个request参数是怎么来的?
request由来:当一个页面被请示时,Django创建一个包含请求元数据的HttpRequest对象。然后Django调入合适的视图,把HttpRequest作为视图函数的第一个参数传入。每个视图要负责返回一个HttpResponse对象。
initialize_request()分析:
这里将原先的requet封装于Request类中
这里标记的3处,分别对应解析器,认证器,渲染器,这里先讲get_authenticators.请看下文