请求与响应
一、请求与响应流程
1. 发起请求
用户在前端页面发起请求(三种请求方式:a标签、form表单、location.href-在地址栏中输入URL)
2. 接收请求
请求从前端页面发送给了服务器(Django程序),请求最终达到了视图函数。而在发请求的过程中,往往会传递一些参数给服务器(从前端传参数给后端) Mr_lee 18
(1) 命名路径/正则路径/正则命名路径
a. 前端URL: http://127.0.0.1:8000/hello/Mr_lee/18/
b. 后端path: path('hello/<name>/<age>/',views.hello),
c. 视图函数: def hello(request,name,age): print(name,age)
(2) request方式
a. 前端URL: http://127.0.0.1:8000/hello/?name=Mr_lee&age=18
b. 后端path: path('hello/',views.hello),
c. 视图函数: def hello(request):
name = request.GET.get('name')
age = request.GET.get('age')
3. 处理请求
一般是获取到前端的参数后,对数据库进行增删改查等操作 - 业务逻辑
4. 返回响应
在视图函数处理完请求后,然后返回响应给前端页面
# 发起请求->接收请求->处理请求->返回响应
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6TUBrjaS-1644897469122)(Django05_请求与响应.assets/image-20210222211636269.png)]
二、HttpRequest对象
1. 当用户向服务器发起一个请求,django会自动创建一个HttpRequest对象,并将请求的参数包装在该对象的GET或POST属性中,然后将该对象作为实参传递给视图函数的形参request.
2. HTTP请求:
HTTP:超文本传输协议,前端页面(浏览器) 和 后端程序(服务器)之间通信的一种规范
从前端页面发起的一个请求就称之为HTTP请求
3. Django的执行过程:
(1)创建对象 - HttpRequest对象
req = HttpRequest()
req.GET['name'] = 'Mr_lee' # 给字典加了一个键值对
req.GET['age'] = 18 # 又加了一个键值对
(2)调用hello函数,并传递实参给形参
hello(req) -> def hello(request): request.GET.get('name')
- HttpRequest对象常用的属性
1. GET 和 POST - 类似于字典 包含着GET请求和POST请求时传递的参数
def hello(request):
request.GET.get('xx') 或
request.POST.get('xx')
2. method 属性 - 表示请求的方法
def hello(request):
if request.method == 'GET':
reuest.GET.get('xx')
elif request.method == 'POST':
request.POST.get('xx')
3. COOKIES - 后续讲解
三、HttpResponse对象
1、Response对象简介
1. HttpResponse对象是指响应对象,它是由程序员`自己创建`的,每一个视图函数都必须返回一个HttpResponse对象
2. request对象和response对象起到了客户端浏览器和服务器之间的`信息传递`的作用。request对象用于接收前端浏览器提交的数据,而response对象的功能则是将服务器的数据发送给前端。
2、创建响应对象
1. 方式一: 不使用模板文件,直接返回
def hello(request):
return HttpResponse('')
2. 方式二:调用模板
def login(request):
return render(request,'login.html') # render的本质还是HttpResponse
四、View的跳转
1、跳转的情景
- 基本的跳转: 从一个view跳转到一个template
def index(request):
return render(request,'index.html')
说明:用户在浏览器地址栏中输入相应的URL,回车时,就是一个空白页面跳转到index.html
- 更为复杂的跳转:从一个view跳转到另一个view,然后再跳转到template
def index1(request):
return render(request,'index.html')
def index2(request):
return redirect('/xxx/index1/')
说明:用户输入index2的URL,调用index2的视图函数,该视图函数并没有直接渲染页面,而是重定向到index1中
# 什么情况下: 用户打开注册页面,注册完成后,自动跳转到登录页面
2、跳转方式
1. 转发: 基本的跳转,浏览器中的URL不变
转发是一个请求内的跳转,用于view到template之间的跳转 render()
2. 重定向:从view跳转到view,浏览器中的URL会发生改变
重定向发生的不同请求之间,用于view与view之间的跳转 redirect(to=要跳转到的页面的URL)
五、Cookie
1、Cookie简介
1. Cookie实际上是一种数据存储技术,由服务器生成(执行),并保存在浏览器中的一种技术。
2. 由于HTTP协议是一种无状态的协议,客户端浏览器和服务器端数据交换完毕后,再次交换数据需要建议新的连接。
比如:登录邮箱,我们点击了“7天自动登录”或“记住我”
2、Cookie的应用场景
- 保存登录信息-用户名和密码- 保存用户的搜索记录
3、Cookie的使用过程
3.1 存储Cookie
存储Cookie需要由Response对象来完成,当通过Response对象设置好Cookie后,再响应到客户端,Cookie就会随之存储到浏览器中。
-
不使用模板
def index(request): resp = HttpResponse('存储cookie') resp.set_cookie('username','Mr_lee') resp.set_cookie('password','123456') return resp
-
使用模板
def index(request): resp = render(request,'index.html') resp.set_cookie('username','Mr_lee') resp.set_cookie('password','123456') return resp
3.2 读取Cookie
# 读取cookie需要由request对象完成# 当向服务器发起一个请求时,request对象会携带浏览器中的所有的cookie到达到服务器def read(request): # print(request.COOKIES) username = request.COOKIES.get('username') password = request.COOKIES.get('password') print(username,password) return HttpResponse('读取cookie')
3.3 Cookie的生命周期
默认的生命周期: 一次会话,浏览器打开到关闭,关闭浏览器后,cookie会失效修改生命周期: resp.set_cookie('username','Mr_lee',max_age=值(秒)) (1) max_age=0 删除cookie (2) max_age=-1 相当于不设置 会话cookie (3) max_age=100 存活100秒 100秒后失效
3.4 Cookie的中文问题
# cookie不能直接存储中文# 存储:username = '吕小布'.encode('utf-8').decode('latin-1')resp.set_cookie('username',username,max_age=3600*24*7)# 读取:username = request.COOKIES.get('username')username = username.encode('latin-1').decode('utf-8')print(username)
4、案例:记住我
登录页面, 记住我(7天自动登录)选项,当用户勾选了“记住我”,点击登录,将用户名和密码保存在cookie中,下次用户就不需要再进行登录操作了
# 在登录逻辑中,登录成功的情况下,并且勾选了记住我def login_logic(request): username = request.POST.get('username') password = request.POST.get('password') rem = request.POST.get('remember') result = User.objects.filter(username=username,password=password) if result: resp = redirect('/account/home/') if rem: # 存储cookie resp.set_cookie('username',username,max_age=3600*24*7) resp.set_cookie('password',password,max_age=3600*24*7) return resp return HttpResponse('登录失败')# 下次输入登录页面的URL,执行login视图函数,在login函数中,需要检查并验证cookie# 如果cookie验证成功,直接跳转到主页面,否则 依然渲染登录页面def login(request): username = request.COOKIES.get("username") password = request.COOKIES.get("password") # 一定要和数据库进行比对 (不能只判断username和password是否存在) result = User.objects.filter(username=username,password=password) if result: return redirect('/account/home/') return render(request,'alogin.html')
六、Session
1、Session简介
1. Cookie将少量数据存储在客户端浏览器本地,而Session是一种将会议状态存储在服务器端的技术(数据库中)。2. Session(翻译:会话)一般指的是浏览器页面从打开到关闭的这段时间。Session的存储技术一般用于多个请求之间,共享数据状态。3. 常用情景: 登录成功后,记录下登录状态(Session),周期为一个会话周期,然后就可以去访问‘我的订单、我的余额’等页面
2、Session的使用
2.1 启用session
1. INSTALLED_APPS中需要有sessions INSTALLED_APPS = [..., 'django.contrib.sessions', ...] 2. MIDDLEWARE = [.., 'django.contrib.sessions.middleware.SessionMiddleware',...]3. 需要执行迁移的指令 python manage.py migrate 生成django_session表 - 存储session数据
2.2 Session生命周期
- 默认生命同期: 默认的生命周期为2周 - 修改:(一般session都会修改为一次会话) 在settings.py文件中添加如下: SESSION_EXPIRE_AT_BROWSER_CLOSE=True
2.3 Session的存储
def save(request): request.session['username']='Tom' request.session['password']='123456' return HttpResponse("存储session")
2.4 Session的读取
def read(request): username = request.session.get('username') password = request.session.get('password') print(username,password) return HttpResponse("读取session")
注意:
-
存储session和读取session都由request对象来完成
-
session没有中文问题
-
session也没有长度限制 (存储在数据库中)
-
session存储在服务器的数据库中相比cookie更安全
2.5 清除session
比如:点击了“退出”,此时需要清除登录状态(session)1. request.session.flush() 清除session所有数据,清除表中的数据及cookie中的sessionid2. request.session.clear() 清除了session_date中的键值对,但session表中的记录还在3. del request.session[key] 删除指定的session中的某一个键值
3、Session的实现原理
1. 浏览器第一次请求session对象时,服务器会创建一个session并生成一个sessionid,存储数据库中(session_key),同时会将sessionid返回给浏览器,存储浏览器的cookie中。2. 在浏览器不关闭的情况下(会话周期),之后的每次请求都会携带cookie中的sessionid到达服务器。服务器接收到请求后就可以通过request对象获取到cookie中的sessionid,然后再通过该id从数据库中找到对应的session_data给请求者使用。# 不同的用户,打开的是自己电脑上的浏览器,去存储sesion时,会给每一个用户生成一个sessionid,并将id返回给用户的浏览器的cookie中# 用户下一次再去请求session数据时,请求会携带自己的浏览器中的sessionid到达服务器,服务器根据sessionid找到对应行的数据
4、session和cookie区别
1. Cookie是将数据存储在浏览器本地,Session是存储在服务器的数据库中2. 需要在一小段时间后依然可以保持的数据,使用Cookie来存储,如“记住我”。保证在一段时间,可以自动登录。 `Cookie一般用于多个会话间的状态保持` 需要在多个请求间多次共享数据,保持状态,使用Session。`Session是一个会话间的状态保持` 3. 每个网站在一个浏览器中的cookie数据上限上4K,而session无限制4. Cookie存在浏览器本地,隐私性不好,安全性较低。而session存储在服务器更安全。5. Cookie生命周期默认为一次会话,但一般会修改为一个固定周期,而Session默认为2周,但一般修改为一次会话。
5、案例:强制登录
当当网: 打开当当网,未登录,直接点击“我的订单”,无法进入,此时会强制跳转到登录页面,登录成功后,再点击“我的订单”就可以进入了。 当访问我的订单时,它会检测登录状态,当登录成功后,需要记录登录状态(Session)
def login(request): username = request.COOKIES.get("username") password = request.COOKIES.get("password") # 一定要和数据库进行比对 (不能只判断username和password是否存在) result = User.objects.filter(username=username,password=password) if result: # 自动登录时,也需要存储登录状态 request.session['is_login'] = True return redirect('/account/home/') return render(request,'alogin.html')def login_logic(request): username = request.POST.get('username') password = request.POST.get('password') rem = request.POST.get('remember') result = User.objects.filter(username=username,password=password) if result: # 手动登录成功时,记录登录状态 request.session['is_login'] = True resp = redirect('/account/home/') if rem: # 存储cookie resp.set_cookie('username',username,max_age=3600*24*7) resp.set_cookie('password',password,max_age=3600*24*7) return resp return HttpResponse('登录失败')def home(request): # 在需要有登录状态的请求中(视图函数中) # 从session中获取登录状态 is_login = request.session.get('is_login') if is_login: return render(request,'home.html') return redirect('/account/login/')
七、全局错误视图设置
# 在项目上线运行时,需要配置错误页面给用户请求与响应: 如果请求发送成功,且服务器成功返回了响应,此时在前端页面中可以得到一个成功的状态码200 HTTP请求的状态码: 200 - 请求成功 301/302 - 重定向 403 - 禁止提交 CSRFToken 404 - 资源未找到Not Found 500 - 服务器错误Internet Server Error
如果发生了404或500等错误,不能让用户看到错误日志 (黄色错误页面)# 解决方案: (1)在settings.py文件中,将Debug=True改为False DEBUG = False # 开发阶段需要设置为True 调试模式 项目上线后,不能让用户看到错误日志 因此需要将调试模式改为False (2) ALLOWED_HOSTS = ['*'] ---------------------------------------------------------------------------------# 配置错误页面: 在templates目录下,新建404.html 500.html即可,当出现访问错误时,django会自动根据错误的状态码找到对应的html文件,展示给用户
八、基于View的事务控制
# 事务控制的目的: 保证数据库中数据的完整性 # 事务加在哪些操作中:增删改
在Django中进行事务控制的两种方式:
- 方式一:
给所有的请求(视图函数)都包裹在一个事务中,在视图函数开始时begin,函数结束时commit
在settings.py文件中的databases中添加:DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'modeldb', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'root', 'PASSWORD':'123456', 'ATOMIC_REQUESTS':True }}# 弊端: 当函数中有try块时,错误会先被try块捕获并处理,此时事务就检测不到错误,无法回滚数据def register_logic(request): try: username = request.POST.get('username') password = request.POST.get('password') user = User.objects.create(username=username, password=password) if user: 10 / 0 return redirect('/account/login/') # 重定向:从一个页面定位到另一个页面 except: return HttpResponse('注册失败')
- 方式二:
在视图函数中通过上下文管理器with transaction.atomic():来进行事务控制
# 在databases中将 'ATOMIC_REQUESTS':True 去掉 def register_logic(request): try: username = request.POST.get('username') password = request.POST.get('password') with transaction.atomic(): user = User.objects.create(username=username, password=password) if user: 10 / 0 return redirect('/account/login/') # 重定向:从一个页面定位到另一个页面 except: return HttpResponse('注册失败') # 第二种方式进行事务控制,更加地灵活,当发生错误时,事务先检测到错误,进行回滚操作,但事务本身不能处理错误,所以错误最终还是会被try块捕获并处理
九、反向解析
1、简介
在django项目中,经常需要获取某个URL,比如:
# 前端页面中: (1)<a href='url'> 跳转 </a>
(2)location.href = 'url'
(3)<form action='url'> </form>
# 后端程序中:
(1)重定向 redirect('要跳转到的页面的URL') 上述中的URL路径的写法都称之为`硬编码`,一旦urls.py
文件中的path访问发生了修改 `path('home/',viwes.home)` -> `path('homepage/',viwes.home)` ,
此时所有用到该访问路径的地方都需要修改。显然这种做法是比较stupid
2、反向解析
此时我们需要一种安全、可靠的、自适应的机制,当urls.py文件中的path访问路径发生改变时,无需要在项目
的源代码到处去搜索修改失效的硬编码的URL路径。
为了解决上述的问题,django提供了解决方案: 只需要在path访问路径中,添加一个name参数,
并赋一个好记的‘字符串’即可 urlpatterns = [ path('home/',views.home,name='home'), ]
3、如何使用
# 1. 在视图函数中 def login(request):
xxx
return redirect('home')
# 这里直接写name参数的值即可 反向解析-django会根据name自动到解析到name对应的url访问路径
# 2. 在模板文件html中 <form action="{% url 'login_logic' %}" method="post"> xx </form>
4、命名空间
如果多个app中出现了同名的name参数,此时有可能引发混乱。一个项目会出现多个相同的name,django在进行反向解析时,
就不知道解析成哪个app中的url解决: 给每个app的urls.py文件中,
添加 app_name='app的名字' 使用时: 前端页面: {% url '空间的名字:name'%} 视图函数: redirect('空间名:name')