视图
概述
- 作用:
视图接受web请求,并响应web请求. - 本质:
视图就是python中的函数 - 响应的内容:
- 网页:
- 重定向
- 错误视图:
404错误.
500错误.(服务器内部请求)
400错误.
- JSON数据
- 网页:
URL配置
配置流程:
指定根级url配置文件:
在settings.py的ROOT_URLCONF选项1
ROOT_URLCONF = 'me.urls'
urlpatterns:
1
2
3
4
5
6
7from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^',include('myapp.urls'))
]一个url实例的列表,元素是url对象
url对象的三参数:
正则表达式
视图名称
名称(之后再讲)
url匹配的注意事项:
如果想从url中获取一个值,需要对正则加小括号形成分组.
匹配正则前方不需要添加反斜杠.
因为都是在最后加的.1
url(r'^students/$',views.index)
引入其他其他url配置:
在应用中创建urls.py文件,定义本应用的url配置.
在工程urls.py文件中使用include()方法1
2
3
4urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^',include('myapp.urls'))
]
ulr的反向解析:
- 概述:
如果在视图,模板中使用了硬编码链接,在rul配置发生改变时,动态生成链接的地址. - 解决:在使用链接时,通过url配置的名称,动态生成url地址
- 作用:使用url模板
- 概述:
视图函数
定义视图:
本质:一个函数.
视图参数:- 一个HttpRequest的实例.
- 通过正则表达式获取的参数.
位置:在views.py中定义
错误视图:
404视图:
找不到网页时返回(url匹配不成功时).
404.html不必匹配视图,在templates目录下定义404.html即可
django会自动给404.html传递一个request_path参数(就是错误的路径)
配置settings.py:
debug:1
2# 如果为True,则永远不会调用404.html
DEBUG = Falseallowed_hosts:
1
2# 允许任何人访问
ALLOWED_HOSTS = [*]
500视图:
在视图代码中出现错误(服务器代码)400视图:
错误出现在客户的操作.
HttpRequest对象
概述:
服务器接收http请求后,会根据报文创建HttpRequest对象.
视图的第一个参数就是HttpRequest.
HttpRequest对象是django创建的,之后调用视图时传递给视图.属性:
path:
请求的完整路径(不包含域名和端口)method:
请求的方式encoding:
就是请求响应头的accept-encoding参数,
表示浏览器提交的数据的编码方式.一般为UTF-8GET:
类似于字典的对象,包含了get请求的所有参数
(比方说,当你传递的url为www.baidu.com/?a=1&b=2A&c=3,get对象就是{'a':1,'b':2,'c':3})POST:
类似于字典的对象,包含了post请求的所有参数.
FIELS:
类似于字典的对象,包含了所有上传的文件.COOKIES:
字典,包含所有的cookiessession:
类似字典的对象,表示当前会话
方法:
is_ajax():
如果是通过XMLHttpRequst发起的,返回TrueQueryDict对象:
- request对象中的GET,POST都属于QueryDict对象
- 方法:
get():作用:根据键获取值
getlist():将键的值易列表的形式返回,可以获取多个值.
GET属性:
获取浏览器传递过来给服务器的数据.如果传入的url为:127.0.0.1/8000/get1?a=1&b=2&c=3
可以使用下面的视图函数获取:1
2
3
4
5
6def get1(request):
# 使用request.GET.get获取abc参数的值
a = request.GET.get('a')
b = request.GET.get('b')
c = request.GET.get('c')
return HttpResponse(a,b,c)如果传入的url为:127.0.0.1/8000/get1?a=1&a=2&c=3(有两个相同的参数)
可以使用下面的视图函数获取:1
2
3
4
5
6def get1(request):
# 使用request.GET.get获取ac参数的值
a = request.GET.getlist('a')
a1,a2 = a[0],a[1]
c = request.GET.get('c')
return HttpResponse(a1,a2,c)
POST属性:
使用表单实现POST请求.
因为表单本身就是一个字典,所有可以使用request.POST.get获取1
2
3
4
5
6
7
8def post(request):
name = request.POST.get('name')
gender = request.POST.get('gender')
age = request.POST.get('age')
# 因为hobby是一个复选框,所以使用getlist
hobby = request.POST.getlist('hobby')
return HttpResponse(name,gender,age,hobby)
HttpResponse对象:
作用:给浏览器返回数据.
HttpRequst对象是由django创建的,HttpResponse对象是由程序员创建.用法:
不调用模板:
1
2
3
4from django.http import HttpResponse
def index(request):
return HttpResponse('hello world')调用模板:使用render方法:
原型:
render(request,templateName [,context])作用:
结合数据和模板,返回完整的HTML页面参数:
request:请求体对象
templateName:模板路径
context:传递给需要渲染在模板上的数据.示例:
1
2
3def students(request):
studentslist = Students.objects.all()
return render(request,'myapp/students.html',{'students':studentslist})属性:
content:返回的内容.(就是响应体)
charset:编码格式
status_code:响应状态码
content-type:
就是响应头的Content-Type: text/html
,
表示返回的内容的类型指定输出的MIME类型
方法:
init:
使用页面内容实例化HttpResponse对象write(content):
以文件的形式写入flush():
以文件的形式输出缓冲区set_cookie(key,value=’’,max_age=None,exprise=None):
设置cookie1
2
3
4
5# 定义一个视图函数,并且为其配置url
def setcookies(request):
res = HttpResponse()
res.set_cookie('cookie_key','cookie_value')
return res当我们访问url就可以得到:
1
2
3
4
5
6# 获取cookie值,并且展示到页面上
def cookietest(reqeust):
res = HttpResponse()
cookie = request.COOKIES
res.write('<h1>'+cookie['cookie_key']+'</h1>')
return resdelete_cookie(key):
删除cookie,如果删除一个不存在的key,就当什么都没发生.- 子类HttpResponseRedirect:
功能:重定向,服务器端跳转
使用:
1
2
3
4
5
6
7
8
9
10
11# 匹配url
url(r'^before$',views.before),
url(r'^after$',views.after),
# 匹配视图
from django.http import HttpResponseRedirect
def before(request):
return HttpResponseRedirect('after')
def after(request):
return HttpResponse('this is after HTML')当我们输入before时,得到:
简写:redirect(to)
其实就是换了一个函数
**to推荐使用反向解析**1
2
3
4
5
6
7
8
9
10
11# 匹配url
url(r'^before$',views.before),
url(r'^after$',views.after),
# 匹配视图
from django.shortcuts import redirect
def before(request):
return redirect('after')
def after(request):
return HttpResponse('this is after HTML')子类JsonResponse:
返回Json数据,一般用于异步请求(ajax)
__init__(self.data)
1
2
3
4
5from django.http import JsonResponse
def jsonresponse(request):
if request.is_ajax():
a = JsonResponse({...})
return a__init__(self.data)中的data:字典对象
JsonResponse的Content-type类型为
application/json
状态保持:
概述:
http协议时无状态的,每次请求都是一次新的请求,他不记得以前的请求.
客户端与服务器的一次通信就是一次会话
实现状态保持,在客户端或者服务端存储有关会话的数据
存储方式:cookie,session.
cookie:所有的数据存储在客户端,不要存敏感的数据.
session:所有数据存储在服务端,在客户端使用cookie存储session_id.简单来说,session是一个字典,敏感数据存储在字典的values中,现在我将字典的key作为cookie返回给客户端.
这样就实现了敏感数据的传输,不会被截获.状态保持的目的:
在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据.- 启用session:
在setting.py的installed_apps,MIDDLEWARE选项:
- 启用session:
1
2
3
4
5
6
7
8
9
10INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
# 默认启用sessions应用
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp'
]1
2
3
4
5
6
7
8
9
10MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# 默认启用sessions中间件
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]- 使用session:
启用session后,每个HttpRequest对象都有一个session属性,他其实就是一个类型字典的对象.
get(key,default=None):
根据键获取session值clear():
清除所有的会话flush()
删除当前会话并删除会话的cookie使用如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 配置url
url(r'^main$',views.main),
url(r'^login$',views.login),
url(r'^showmain',views.showmain)
# 视图函数:
def main(request):
username = request.session.get('username','游客')
return render(request,'myapp/main.html',{'username':username})
def login(request):
return render(request,'myapp/login.html')
def showmain(request):
username = request.POST.get('username')
request.session['username'] = username
return HttpResponseRedirect('main')1
2
3
4
5
6
7
8
9
10
11
12
13<!-- templates/myapp/main.html -->
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>我的</title>
</head>
<body>
<h1>欢迎:{{username}}</h1>
<a href="/login">登录</a>
</body>
</html>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!-- templates/myapp/login.html -->
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<!-- action会跳转到/showmain页面 -->
<form action="/showmain" method="post">
<input type="text" name="username">
<input type="submit" value="登录">
</form>
</body>
</html>还可以添加一个退出登录:
1
2
3
4
5
6
7
8
9
10
11# 匹配url
url(r'^quit$',views.quit)
# 退出登录的视图
from django.contrib.auth import logout
def quit(request):
# 清除session,建议使用这种方法
logout(request)
# 或者使用:request.session.clear()
# 或者使用:request.session.flush()
return HttpResponseRedirect('main')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!-- 添加一个退出登录的按钮 -->
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>我的</title>
</head>
<body>
<h1>欢迎:{{username}}</h1>
<a href="/login">登录</a>
<a href="/quit">退出登录</a>
</body>
</html>点击退出登录
设置过期时间:
session默认寿命:两个星期.修改使用set_expiry(value)函数
1
2
3
4def showmain(request):
username = reqeust.POST.get('username')
# 设置10秒寿命
request.sessionn.set_expiry(10)value的值:
- 整数,表示秒
- 时间对象
- 0,表示关闭浏览器是失效
- None,永不过期
存储session的位置:
默认存储在数据库中:
1
2
3
4
5
6
7
8
9INSTALLED_APPS = [
# 启用 sessions 应用
'django.contrib.sessions',
]
MIDDLEWARE = [
# 启用 Session 中间层
'django.contrib.sessions.middleware.SessionMiddleware',
]1
2
3# /setting.py
# SESSION_ENGINE的默认值为:
SESSION_ENGINE = 'django.contrib.sessions.backends.db'如果要修改session的存储方式.必须修改SESSION_ENGINE
保存到存缓:1
2# 只存储在内容中,速度比存储在数据库快,若丢失了则无法找回.
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'保存到存缓和数据库:
1
2# 优先从内存获取,若读取失败再去数据库中读取
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'保存到文件:
1
2
3SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# 可选配置
SESSION_FILE_PATH = '/monkey/www/'保存到cookies:
1
2
3SESSION_ENGINE = 'jango.contrib.sessions.backends.signed_cookies'
# 建议配置,阻止 javascript 对会话数据的访问,提高安全性。
SESSION_COOKIE_HTTPONLY= True
使用redis存缓session:
1
2# 先安装依赖库
pip install django-redis-sessions1
2
3
4
5
6
7# /setting.py
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 0
SESSION_REDIS_PASSWORD = ''
SESSION_REDIS_PREFIX = 'session'
模版
概述
- 模板由两部分组成:
- html代码
- 逻辑控制代码
- 作用:
快速生成HTML页面 - 优点:
- 模板的设计实现了业务逻辑和现实内容的分离
- 视图可以使用任何模板
- 模板处理:
- 加载
- 渲染
定义模板
变量:
- 视图传递给模板的数据
- 语法:
- 注意:
如果使用的变量不存在,则插入的是空字符串 - 在模板中使用点语法:
- 字典查询
- 属性或者方法
- 数字索引
- 在模板中对象的方法:
在模板里确实能使用对象的方法,但是不能传递参数.
(简单来说,就是不能调用有参数的方法)
标签:
语法:
{% tag %}
作用:
- 在输出中创建文本
- 控制逻辑和循环
if
格式:
1
2
3
4
5
6
7{% if 表达式 %}
语句
{% elif 表达式 %}
语句
{% else 表达式 %}
语句
{% endif %}示例:
1
2
3{% if num %}
<h1>hello world</h1>
{% endif %}
for:
格式:
1
2
3{% for 变量 in 列表 %}
语句
{% endfor %}1
2
3
4
5
6<!-- 列表为空或者列表为空或者不存在时执行语句2 -->
{% for 变量 in 列表 %}
语句1
{% empty %}
语句1
{% endfor %}1
2
3
4<!-- 这不是语句,而是一个变量 -->
<!-- 表示当前是第几次循环 -->
<!-- 一般在for循环体里使用 -->
{{forloop.counter}}示例:
1
2
3
4
5{% for stu in students %}
<li>{{forloop.counter}}--{{stu.name}}</li>
{% empty %}
<li>目前没有学生</li>
{% endfor %}
comment:
作用:多行注释
格式:
1
2
3{% comment %}
注释的内容
{% endcomment %}示例:
1
2
3
4{% comment %}
<h1>被注释</h1>
{{stu.name}}
{% endcomment %}
ifequal、ifnotequal:
判断是否相等,是否不相等
格式:
1
2
3
4<!-- 值1==值2则执行语句-->
{% ifequal 值1 值2 %}
语句
{% endifequal %}
include:
作用:
加载模板并以标签内的参数渲染格式:
1
{% include '模板目录' 参数1 参数2 %}
url:
作用:
反向解析格式:
1
{% url 'namespace.name' p1 p2 %}
csrf_token:
作用:
用于跨站请求伪造保护格式:
1
{% csrf_token %}
block、extends
- 作用:
用于模板的继承
- 作用:
autoescape
- 作用:
用于HTML转义
- 作用:
过滤器:
语法:
1
{{var|过滤器}}
作用:
在变量被显示前修改它,但是并不修改这个变量示例:
1
2
3
4
5
6
7
8
9
10
11
12{% comment %}
{{grade.headteacher}}的值是hyl
{{grade.headteacher|upper}}的值是HYL
{% endcomment %}
<li>
<!-- 输出hyl -->
{{grade.headteacher}}
<!-- 输出HYL -->
{{grade.headteacher|upper}}
</li>过滤器其实就是函数.
过滤器可以传递参数,参数使用引号引起来.join:
和python的join的功能是一样的格式:
1
{{var|default:'good'}}
示例:
1
2
3
4
5
6<h1>
{{the_list|join:'#'}}
</h1>
<!-- the_list=['hyl','123','456'] -->
<!-- 返回:hyl#123#456 -->
如果一个变量没有被提供.或者值为false,空.可以使用默认值.
default:格式:
1
{{var|default:'good'}}
示例
1
2
3
4<h1>
{{test_num|default:'没有'}}
</h1>
<!-- 如果没有test_num这个变量,就会使用默认值'没有' -->
根据给定格式转换日期为字符串:
date:格式:
1
{{dateVal|date:'y-m-d'}}
HTML转义:
escape加减乘除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!-- 加减是过滤器 -->
<!-- 加 -->
<h1>{{num|add:10}}</h1>
<!-- 减 -->
<h1>{{num|add:-10}}</h1>
<!-- 乘除不是过滤器,是一个标签 -->
<!-- 乘:num乘以5 -->
<!-- 实际上是(num/1)*5 -->
<h1>{% widthratio num 1 5 %}</h1>
<!-- 除:num除以2 -->
<!-- 实际上是(num/2)*1 -->
<h1>{% widthratio num 2 1 %}</h1>
注释:
单行注释:1
{# 注释内容 #}
多行注释:
1
2
3
4{% comment %}
<h1>被注释</h1>
{{stu.name}}
{% endcomment %}
反向解析
实现功能在index.html页面点击超链接,然后跳转到good.html
代码如下:1
2
3
4
5
6
7
8
9
10
11# project/urls.py
urlpatterns = [url(r'^',include('myapp.urls'))]
# myapp/urls.py
urlpatterns = [
url(r'^good/(\d+)$',views.good),
url(r'^index$',views.index)]
# myapp/views.py
def good(request,num):
return render(request,'myapp/good.html',{'num':num})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24{# templates/myapp/index.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<a href="/good/188">链接</a>
</body>
</html>
{# templates/myapp/good.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>good</title>
</head>
<body>
good---{{num}}
</body>
</html>现在确实能获取网页.
但是,假如某天project/urls.py的根路径改变了
1
2
3
4# project/urls.py
urlpatterns = [url(r'^',include('myapp.urls'))]
# 改变为
urlpatterns = [url(r'^me',include('myapp.urls'))]那么全部就都会报废.出现问题的根本原因:
这里是根据a标签的路径<a href="/good/188">链接</a>
得出url为/good/188
,然后再根据这个url去寻找视图.这时候urls.py中可能没有这个url,所以也就找不到视图.解决方法:反向解析
我们不再根据url去找视图,而是直接调用想要的视图示例,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# project/urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 注意这里不是r'^'而是r'^hyl/'
# 使用include的namespace参数
url(r'^hyl/',include('myapp.urls',namespace='myapp'))
]
# myapp/urls.py
urlpatterns = [
# 使用url的name参数
url(r'^good/(\d+)$',views.good,name='good'),
url(r'^redict/$',views.redict)
]
# 两个视图函数不变
def good(request,num):
return render(request,'myapp/good.html',{'num':num})
def redict(request):
return render(request,'myapp/redict.html')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26{# templates/myapp/good.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>good</title>
</head>
<body>
good---{{num}}
</body>
</html>
{# templates/myapp/redict.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>redict</title>
</head>
<body>
{# 注意这里就是:直接调用myapp的good对应的视图 #}
<a href="{% url 'myapp:good' 199 %}">链接</a>
</body>
</html>结果:
使用
<a href="{% url 'myapp:good' 199 %}">链接</a>
直接跳转到127.0.0.:8000/hyl/good/199
上一个例子是使用:<a href="/good/188">链接</a>
这个url为127.0.0.:8000/good/199,
这是找不到对应的视图的.总结:
使用反向解析只需要修改三个地方:1
2
3
4
5
6# project/urls.py
url(r'^hyl/',include('myapp.urls',namespace='myapp'))
# myapp/urls.py
url(r'^good/(\d+)$',views.good,name='good')
# templates/good.html
<a href="{% url 'myapp:good' 199 %}">链接</a>
模板继承
作用:
减小页面的内容的重复定义,实现页面的重用block标签:
在父模板中预留区域,子模板去填充.
语法:
1
2
3{%block 标签名 %}
...
{%endblock 标签名 %}
extends标签:
继承模板,需要写在模板文件的第一行
语法:
1
{% extends '父模板的路径' %}
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32{# templates/myapp/base.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#header{
width: 100%;
height: 100px;
background-color: red;
}
#footer{
width: 100%;
height: 100px;
background-color: bule;
}
</style>
</head>
<body>
<div id='header'>header</div>
<div>
{% block hyl %}
{% endblock hyl %}
</div>
<div id='footer'>footer</div>
</body>
</html>1
2
3
4
5
6{# templates/myapp/test.html #}
{% extends 'myapp/base.html' %}
{% block hyl %}
<h1>hello world</h1>
{% endblock hyl %}配置路由,视图.得到结果:
HTML转义
问题,现在我们在视图函数中传递html代码作为参数
1
2def index(request):
return render(request,'myapp/index.html',{'code':'<h1>heyingliang</h1>'})1
2
3
4
5
6
7
8
9
10<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
{{code}}
</body>
</html>得到结果
Django会将接收到的code当成普通字符串渲染.
现在想要将接收到的字符串当成HTML代码渲染.过滤器:
escape:转成字符串
1
2{# 将传来的code当成字符串渲染 #}
{{code|escape}}safe
1
2{# 将传来的code当成代码渲染 #}
{{code|safe}}
标签:
autoescape:上面的过滤器确实可以开启和关闭HTML转义.
但是不够方便,这时可以使用qutoescape:
语法:1
2
3
4{# off/on表示开启/关闭转义 #}
{%autoescape off%}
{{code}}
{%endautoescape%}示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
{# 开启转义 #}
{%autoescape off%}
{{code}}
{%endautoescape%}
{# 关闭转义 #}
{%autoescape on%}
{{code}}
{%endautoescape%}
</body>
</html>
CSRF
- 概念解释:
CSRF, Cross Site Request Forgery,跨站点伪造请求。
某些恶意网站包含链接,表单,按钮,js利用登录用户在浏览器中认证,从而攻击服务器.
举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,而且与你的网站的session 尚未过期,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造
但是我们在使用post发起请求的时候就有可能csrf校验失败错误,此时request请求被丢弃掉。
防止CSRF:
出现上面错误的原因就是Django默认开启了防止CSRF,把正确的post请求都防下来了
1
2
3
4
5# project/settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
...
]虽然我们可以通过关闭这个应用来取消,此时将不会进行csrf的校验,但如前面所述,这是一种不安全的行为。而且djano也不推荐使用。
第二种方式:
使用csrf_toke标签1
{% scrf_token %}
也就是说是:
在网页中加入csrf_token的标签就可以通过csrf校验示例:
1
2
3
4
5
6
7
8
9<form action='/showinfo/' method='post'>
{# 仅仅是添加了这句 #}
{% scrf_token %}
姓名:<input type='text' name='username'/>
<hr/>
密码:<input type='password' name='pwd'/>
<hr/>
<input type='submit' value='登录'/>
</form>Django 提供的 CSRF 防护机制:
- django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击。
- 在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token,在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成)
- 在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.
- 在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值
验证码
作用:
在用户注册,登录页面的时候使用,防止暴力请求.减轻服务器的压力.还是防止CSRF的一种方式.示例:
1
2
3
4
5
6
7
8# myapp/urls.py
# 验证码图片的url
url(r'^verifycode/$',views.verifycode),
# 呈现验证码的网址
url(r'^show_verifycode/$',views.show_verifycode),
# 验证验证码的网址
url(r'^verifycodecheck/$',views.verifycodecheck)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63# mypp/views.py
# 制作验证码
def verifycode(request):
#引入绘图模块
from PIL import Image, ImageDraw, ImageFont
#引入随机函数模块
import random
#定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), random.randrange(20, 100))
width = 100
height = 50
#创建画面对象
im = Image.new('RGB', (width, height), bgcolor)
#创建画笔对象
draw = ImageDraw.Draw(im)
#调用画笔的point()函数绘制噪点
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill)
#定义验证码的备选值
str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
#随机选取4个值作为验证码
rand_str = ''
for i in range(0, 4):
rand_str += str[random.randrange(0, len(str))]
#构造字体对象
font = ImageFont.truetype(r'C:\Windows\Fonts\AdobeArabic-Bold.otf', 40)
#构造字体颜色
fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
#绘制4个字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
#释放画笔
del draw
#存入session,用于做进一步验证
request.session['verifycode'] = rand_str
#内存文件操作
import io
buf = io.BytesIO()
#将图片保存在内存中,文件类型为png
im.save(buf, 'png')
#将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')
def show_verifycode(request):
return render(request,'myapp/verifycode.html')
def verifycodecheck(request):
userinput = request.POST.get('verifycode').upper()
ture_val = request.session['verifycode'].upper()
if userinput == ture_val:
return render(request,'myapp/verifycode_success.html')
else:
ifsuccess = '失败,请重新输出'
return render(request,'myapp/verifycode.html',{'ifsuccess':ifsuccess})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29{# verfifycode.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>verifycode</title>
</head>
<body>
<form method="post" action='/verifycodecheck/'>
<input type="text" name="verifycode"/>
<img src="/verifycode/">
<input type="submit" name="登录">
<span>{{ifsuccess}}</span>
</form>
</body>
</html>
{# verifycode_success.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>verifycode</title>
</head>
<body>
成功
</body>
</html>效果:
如果使用重定向的话:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def show_verifycode(request):
# 在这里获取session的值
f = request.session.get('flag',True)
str = ''
if f == False:
str = '验证失败,请重新输入'
# 注意要清除session
request.session.clear()
return render(request,'myapp/verifycode.html',{'flag':str})
def verifycodecheck(request):
userinput = request.POST.get('verifycode').upper()
ture_val = request.session['verifycode'].upper()
if userinput == ture_val:
return render(request,'myapp/verifycode_success.html')
else:
# 在这里给session赋值
request.session['flag'] = False
# 重定向
return HttpResponseRedirect('/show_verifycode/')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{# verfifycode.html #}
<html lang='cn'>
<head>
<meta charset="UTF-8">
<title>verifycode</title>
</head>
<body>
<form method="post" action='/verifycodecheck/'>
<input type="text" name="verifycode"/>
<img src="/verifycode/">
<input type="submit" name="登录">
<span>{{flag}}</span>
</form>
</body>
</html>简单来说,如果要使用重定向的话,就类似于scrapy的Meta参数.==Django是使用session来传递参数的.==