Django中间件案例由浅入深+实战
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Django中间件案例由浅入深+实战
WEB前端开发社区昨天
一般概念
中间件允许您在一个浏览器的请求在到达Django视图之前处理它,以及在视图返回的响应到达浏览器之前处理这个响应。
Django为每个项目保留一个中间件列表。
您可以在设置文件settings.py中找到它MIDDLEWARE。
每个新的Django项目已经在该列表中添加了一堆中间件,在大多数情况下,您不应从该列表中删除任何中间件。
但是,您可以添加自己的。
中间件的应用顺序与添加到Django设置列表中的顺序相同。
浏览器发送请求时,将按以下方式处理:
•
Browser -> M_1 -> M_2 -> ... -> M_N -> View
视图接收请求,执行一些操作,然后返回响应。
在进入浏览器的过程中,响应必须再次通过每个中间件,但顺序相反:
•
Browser <- M_1 <- M_2 <- ... <- M_N <- View
这是一个非常简短的解释。
更详细的描述可以在Django文档中找到。
一个简单的例子
我们将从一个简单的中间件例子开始,该中间件就算处理请求所花费的时间。
这篇文章中的所有示例都使用Django 3.0.5和Python 3.6.9。
项目设置
首先,使用单个应用程序创建一个Django项目。
忽略数据库迁移,因为本文中的示例将不使用数据库。
在您的应用程序app目录下
创建一个名为middleware.py的文件,这是我们放置大多数代码的地方。
•
•
•
•
django-
admin startproject django_middlewarecd django_middlewarepy thon manage.py startapp introtouch intro/middleware.py 整个项目的目录结构如下所示:
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
django_middleware/├──django_middleware│├── asgi.p y│├──__init__.py│├──settings.py│├──urls.py│└── w
sgi.py├──intro│├──admin.py│├──apps.py│├── __init_ _.py│├──middleware.py│├──migrations││└── __init_ _.py│├── model s.py│├──tests.py│└──views.py└── ma nage.py
不要忘记在django_middleware/settings.py中注册您的应用程序(app):
•
•
•
•
INSTALLED_APPS = [ 'intro', ...]
现在你可以运行你的项目了。
•
python manage.py runserver
编写Django中间件
根据Django文档,有两种编写中间件的方法:函数和类。
我们将使用第一种方法,但最后一个示例还将向您展示如何使用类编写中间件。
Django中间件的一般结构如下所示(示例取自Django文档):
•
•
•
•
•
•
•
•
def simple_middleware(get_response): # 一次性设置和初始化 def middleware(request): # 请求在到达视图前执行的代
码 response = get_response(request) # 响应在返回给客户端前执行的代码 return response return middleware 当Django初始化中间件并将其添加到项目所有中间件的列表中时,该函数simple_middleware将被调用一次。
服务器处理每个请求都会调用middleware该函数。
当请求从浏览器发送到服务器时,将调用response = get_response(request)该行之前的所有内容。
当响应从服务器返回到浏览器时,将调用response = get_response(request)此行之后的所有内容。
那么这条分界线respone = get_response(request)做什么的?简而言之,它将调用列表中的下一个中间件。
如果这是最后一个中间件,则将调用该视图。
该视图接收请求,执行一些操作并生成响应。
然后将该响应返回到列表中的最后一个中间件,然后向上执行直到不再有中间件时将该响应发送到浏览器。
注意:settings.py里中间件的顺序很重要。
更多中间件知识见:
o Django基础(33): 中间件(middleware)的工作原理和应用场景举例
o
在我们的示例中,我们要检查处理请求的整个过程需要多长时间。
像这样编辑文件intro/middleware.py:
•
•
•
•
•
•
•
•
•
•
import time
def timing(get_response): def middleware(request): t1 = time.time() response = get_response(request) t2 = time.time() print("TOTAL TIME:", (t2 - t1)) return response return middleware
在此示例中,我们以秒为单位(time.time())测量请求前后的时间,并打印出时间差异。
下一步是安装中间件,以使Django知道我们将使用它。
我们要做的就是将其添加到django_middleware/settings.py:
•
•
•
•
MIDDLEWARE = [ 'intro.middleware.timing' , ... ]
注意:在此示例中,intro是Django应用程序middleware的名称,是包含我们的代码的Python文件timing的名称,并且是该文件中的中间件函数的名称。
现在我们准备测试它。
打开浏览器并导航到localhost:8000。
在浏览器中,您应该看到默认的Django项目页面(带有火箭的页面)。
在命令行(您在其中调用p ython manage.py runserver)中,您应该看到类似以下内容:
•
•
TOTAL TIME: 0.0013387203216552734[04/Apr/2020 17:15:3 4] "GET / HTTP/1.1" 200 16351
修改请求
我们的中间件效果很好,可以将信息打印到命令行。
但是我们可以走得更远:如何向请求中添加内容,以便我们的视图或模板以后可以使用它?由于我们从事计时业务,如何添加请求发生的日期和时间?
此修改将非常容易。
intro/middleware.py像这样编辑文件:
•
•
•
•
•
•
•
•
•
•
•
•
import time import datetime
def timing(get_response): def middleware(request): request.current_time = datetime.datetime.now() t1 = time.time() response = get_response(request) t2 = time.time() print("TOTAL TIME:", (t2 - t1)) return response return middleware
我们添加了2行:import datetime和request.current_time = datetime.datetime.now()。
他们一起将当前时间添加到我们的请求对
象中。
现在,我们需要一个视图来显示该时间。
编辑intro/views.py:•
•
•
•
from django.http import HttpResponse
def showtime(request): return HttpResponse('Request time is: {}'.format(request.current_time))
对于这样一个简单的示例,我们不需要模板,我们可以直接在代
码中创建HttpResponse对象。
现在,我们需要一个URL作为视图。
创建一个文件intro/urls.py 并编辑它:
•
•
•
•
•
•
from django.urls import path from .views import showtime urlpatterns = [ path('', showtime),]
记住也要编辑django_middleware/urls.py:
•
•
•
•
•
•
•
from django.contrib import admin from django.urls import include, path
urlpatterns = [ path('', include('intro.urls')), path('admin/', admin.site.urls),]
让我们测试一下。
localhost:8000在浏览器中打开。
您应该会看到以下内容:多次刷新页面,以检查您是否将获得不同的结果(每次请求的时间应有更新)。
更有用的东西:处理异常
现在该举一个更有趣的例子了。
考虑一下这种现实情况:您编写
了一个程序,但它不起作用。
发生在我们当中最好的,别担心。
那你通常做什么?您是否会搜索Stackoverflow上的答案?您可能会这样做,实际上所有编码人员都这样做。
我们如何创建可以为我们进行搜索的中间件呢?
Django中间件可以包含一个在每次引发异常时都会调用的函数。
该函数process_exception被调用,并带有两个参数:引起异常的请求(request)和异常本身(exception)。
如果我们将中间件定义为一个函数,那么我们可以process_exception像这样实现:
•
•
•
•
•
•
•
•
•
•
def simple_middleware(get_response): def middleware(request): return get_response(request) def process_exception(request, exception): # Do something useful with the exception pass
middleware.process_exception = process_exception return middleware
在我们的案例中,我们想将异常发送到StackOverflow并获取最相关问题答案的链接。
API简介
如果您以前没有使用过API,请不要担心。
总体思路是:就像您使
用网络浏览器将问题发送到Internet一样,API是通过代码向网站自动发送问题的一种方式。
Stack Exchange提供了用于查询其网站问题的API。
基本url为/2.2/search,之后可以放入搜索参数。
所以,如果你想查询标签上有python,标题中有django的StackOverflow上3个top搜索结果(按票数排序),你可以发送这样的请求:/2.2/search?site=stackoverflow&pagesize=3&sort=votes&order =desc&tagged=python&intitle=django。
继续并在浏览器中检查它。
您应该会看到以下内容:
在Python中,要发送这样的请求,我们将使用一个第三方包,名为requests
Stackoverflow中间件
让我们创建一个新的中间件,名为stackoverflow:
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
import requests from django.http import HttpResponse
# Previous imports and timing middleware should remain unchanged def stackoverflow(get_response): def middleware(request): # This method does nothing, all we want is exception processing return get_response(request) def process_exception(request, exception): url =
'/2.2/search'params = { 'site': 'stackoverflow', 'order': 'desc', 'sort': 'votes', 'pagesize': 3, 'tagged':
'python;django', 'intitle': str(exception), } response
= requests.get(url, params=params) html = ''for question
in response.json()['items']: html += '<h2><a
href="{link}">{title}</a></h2>'.format(**question) return HttpResponse(html)
middleware.process_exception = process_exception
return middleware
每当视图引发异常时,process_exception都会调用我们的方法。
我们使用该requests模块来调用Stack Exchange API。
大多数参数
是不言自明的。
它们与我们在浏览器示例中使用的相同,但不是让它
们全部手动放置在URL中,而是让requests模块为我们完成。
我们只是更改了标签(以搜索Python和Django),然后将异常用作字符串(str(exception))来搜索可用问题的标题。
得到Stack Overflow的响应后,我们将HTML放在一起,其中包含指向每个相关问题的链接。
希望我们可以在那里找到解决问题的答案。
最后,该HTML返回到浏览器。
请注意,来自Stack Overflow的响应不是正常的网页,而是一堆名为JSON格式的信息。
这就是为什么我们使用response.json()获得我们的结果。
当然,我们需要注册这个新的中间件:
•
•
•
•
•
MIDDLEWARE = [ 'intro.middleware.stackoverflow', 'intr o.middleware.timing', ...]
我们现在唯一的问题是我们的views工作起来很顺利,没有异常发生。
如果我们希望新的中间件要处理一些异常,则需要对其稍作修改。
编辑intro/views.py:
•
•
•
def showtime(request): raise Exception('Django middlewar e') # return HttpResponse('Request time is: {}'.format(request.c urrent_time))
请记住,process_exception仅对于实际发生的异常调用该方法。
返回HttpResponseServerError或任何其他错误代码不计算在内。
现在该进行测试了。
在浏览器中打开localhost:8000。
您应该会
看到以下内容:我们刚创建的中间件比初始示例要复杂一些。
随着代
码的增长,将中间件作为类而非函数进行管理可能是一个更好的主意。
我们现在以类重写Stack Overflow中间件,如下所示:
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
class StackOverflow(): def__init__(self, get_response): self.get_response = get_response
def__call__(self, request): return self.get_response(request)
def process_exception(self, request, exception): url =
'/2.2/search'params = { 'site': 'stackoverflow', 'order': 'desc', 'sort': 'votes', 'pagesize': 3, 'tagged':
'python;django', 'intitle': str(exception), } response
= requests.get(url, params=params) html = ''for question
in response.json()['items']: html += '<h2><a
href="{link}">{title}</a></h2>'.format(**question) return HttpResponse(html)
大多数代码看起来相似,但是对于一个类,我们需要将
get_response回调存储在我们的实例中,并在每个__call__方法调用
中使用它。
如果您喜欢此版本,请不要忘记更改设置:
•
•
•
•
MIDDLEWARE = [ 'intro.middleware.StackOverflow', ...]
结论
这些是非常简单的示例,但是中间件可用于许多其他事情,例如
进行权限校验。
我相信您自己可以找到很多想法,希望这篇文章可以
帮助您。
如果您认为缺少某些东西或发现错误,请告诉我!声明:本
文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请
联系我们删除或授权事宜。
喜欢此内容的人还喜欢
从零搭建mysql的主从复制
从
零
搭
建
m
y
s
的
主
从
复
制
.
..
互联网专栏
不喜欢
不看的原因
确定
•内容质量低
•
•不看此公众号
利用sqlmapapi批量检测
利
用
s
ql
m
a
p
a
pi
批
检
测
.
..
林三岁网络安全实验室
不喜欢
不看的原因
确定
•内容质量低
•
•不看此公众号
彻底解决mysql的ONLY_FULL_GROUP_BY错误
彻
底
解
决
m
y
s
ql
的
O
N
L
Y
F
U
L
L
_
G
R
O
U
P
_
B
Y
错
误
.
..
互联网专栏
不喜欢
不看的原因
确定
•内容质量低
•
•不看此公众号。