Loading

Django框架深入了解_01(Django请求生命周期、开发模式、cbv源码分析、restful规范、跨域、drf的安装及源码初识)

一、Django请求生命周期:

前端发出请求到后端,通过Django处理、响应返回给前端相关结果的过程

先进入实现了wsgi协议的web服务器--->进入django中间件--->路由f分发--->视图(CBV或FBV)---->取模板,取数据,用数据渲染模板--->返回模板的字符串---->前端页面得到数据展现页面给用户

二、WEB开发模式:

在开发Web应用中,有两种应用模式:

前后端不分离
前后端分离
1 前后端不分离
在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示,前端与后端的耦合度很高。

这种应用模式比较适合纯网页应用,但是当后端对接App时,App可能并不需要后端返回一个HTML网页,而仅仅是数据本身,所以后端原本返回网页的接口不再适用于前端App应用,为了对接App后端还需再开发一套接口。

2 前后端分离
在前后端分离的应用模式中,后端仅返回前端所需的数据,不再渲染HTML页面,不再控制前端的效果。至于前端用户看到什么效果,从后端请求的数据如何加载到前端中,都由前端自己决定,网页有网页的处理方式,App有App的处理方式,但无论哪种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可。

在前后端分离的应用模式中 ,前端与后端的耦合度相对较低。

在前后端分离的应用模式中,我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。

三、cbv源码分析:

cbv--基于类的视图,一开始我们写视图层的时候是基于函数来处理前端请求数据FBV,这只是一种方法,更常用的还有在视图层使用类的方法来建立视图层

cbv流程:

创建路由

url(r'^test/', views.Test.as_view()),

--->创建类的视图

class Test(View):
    def get(self,request):
        return HttpResponse('cbv_get')
    
    def post(self,request):
        return HttpResponse('cbv_post')

前端请求通过中间件进入路由--->根据路由匹配,成功后会执行后面的Test.as_view()函数,而as_view()在Django启动的时候就会自动执行返回一个view的函数内存地址,此时路由匹配成功就会view加括号执行该函数,而view内部又调用了self.dispatch函数方法,dispatch会根据请求的方式不同,执行我们定义的类中对应请求名称的函数方法(比如get请求,就会执行类中get的函数方法)

四、认识RESTful

即Representational State Transfer的缩写。维基百科称其为“具象状态传输”,国内大部分人理解为“表现层状态转化”。

RESTful是一种开发理念。维基百科说:REST是设计风格而不是标准。 REST描述的是在网络中client和server的一种交互形式;REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口),一种万维网软件架构风格。

我们先来具体看下RESTful风格的url,比如我要查询商品信息,那么

非REST的url:http://.../queryGoods?id=1001&type=t01

REST的url: http://.../t01/goods/1001

可以看出REST特点:url简洁,将参数通过url传到服务器,而传统的url比较啰嗦,而且现实中浏览器地址栏会拼接一大串字符,想必你们都见过吧。但是采用REST的风格就会好很多,现在很多的网站已经采用这种风格了,这也是潮流方向,典型的就是url的短化转换。

那么,到底什么是RESTFul架构: 如果一个架构符合REST原则,就称它为RESTful架构。

要理解RESTful架构,理解Representational State Transfer这三个单词的意思。

具象的,就是指表现层,要表现的对象也就是“资源”,什么是资源呢?网站就是资源共享的东西,客户端(浏览器)访问web服务器,所获取的就叫资源。比如html,txt,json,图片,视频等等。

表现,比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。

浏览器通过URL确定一个资源,但是如何确定它的具体表现形式呢?应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

状态转换, 就是客户端和服务器互动的一个过程,在这个过程中, 势必涉及到数据和状态的变化, 这种变化叫做状态转换。

互联网通信协议HTTP协议,客户端访问必然使用HTTP协议,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。

HTTP协议实际上含有4个表示操作方式的动词,分别是 GET,POST,PUT,DELETE,他们分别对应四种操作。GET用于获取资源,POST用于新建资源,PUT用于更新资源,DElETE用于删除资源。GET和POST是表单提交的两种基本方式,比较常见,而PUT和DElETE不太常用。

而且HTTP协议是一种无状态协议,这样就必须把所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)

总结
综合上面的解释,RESTful架构就是:

每一个URL代表一种资源;
客户端和服务器之间,传递这种资源的某种表现层;
客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

RESTful规范

  • API与用户的通信协议,总是使用HTTPs协议
  • 域名 
    • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
    • https://example.org/api/                         API很简单
  • 版本
    • URL,如:https://api.example.com/v1/
    • 请求头                                                  跨域时,引发发送多次请求
  • 路径,视图网络上任何东西都是资源,均使用名词表示(可复数)
    • https://api.example.com/v1/zoos
    • https://api.example.com/v1/animals
    • https://api.example.com/v1/employees
  • method,通过请求方式method 区分是什么操作
    • GET      :从服务器取出资源(一项或多项)
    • POST    :在服务器新建一个资源
    • PUT/PATCH   :在服务器更新资源
    • DELETE :从服务器删除资源
  • 过滤,通过在url上传参的形式传递搜索条件
    • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
    • https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
    • https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
    • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
    • https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
  • 状态码
    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    
    更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  • 错误处理,应返回错误信息,error当做key。
    {
        error: "Invalid API key"
    }
  • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档
  • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}

 补充知识:跨域

浏览器的同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。”

简单来说就是:好比在一个浏览器上访问两个不同的网站,这两个网站页面有自己的脚本,他们在访问请求服务器资源时,浏览器会检查它们属于哪个网站或者说服务器,如果它跨服务器访问了,那么浏览器就拒绝这种请求

浏览器安全的基石是 同源策略,什么是同源策略呢?言简意赅,就是三个相同:

协议相同。
域名相同。
端口相同。
这三者相同的情况下,才被称作 “同源”,同源策略,可以保证你的服务器的接口是隐私的。

跨域的例子:

端口不同:

假设,你的电脑现在有开启了两个服务器,分别在 不同的端口

http://localhost:8080/index.html    // 发送 ajax 请求

http://localhost:3000    //         提供接口

在这里,这个服务器并没有遵守 同源策略 了,此时,就产生了跨域问题。

协议不同:

举一个很常见的例子,双击打开一个 html 文件,你会看到

file:///Users/xxx/Documents/index.html    // 客户端
同时在浏览器上你会看到: http://localhost:3000 // 服务器

这就是简单的 协议不同 的跨域例子,这个例子,也是我们最好实现的例子 。。。

域名不同:

这个更明显了:

https://www.xxx.com/index.html    // 客户端

https://www.yyy.com        // 服务器

解决方案

1.安装django-cors-headers

pip3 install django-cors-headers

2.配置settings.py文件

INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
 ] 

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware', # 注意顺序
    ...
)
#跨域增加忽略
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
    '*'
)

CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)

CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'Pragma',
)

其他解决方案

1.使用JSONP

使用Ajax获取json数据时,存在跨域的限制。不过,在Web页面上调用js的script脚本文件时却不受跨域的影响,JSONP就是利用这个来实现跨域的传输。因此,我们需要将Ajax调用中的dataType从JSON改为JSONP(相应的API也需要支持JSONP)格式。 
JSONP只能用于GET请求。

2.直接修改Django中的views.py文件

修改views.py中对应API的实现函数,允许其他域通过Ajax请求数据: 

def myview(_request): 
response = HttpResponse(json.dumps({“key”: “value”, “key2”: “value”})) 
response[“Access-Control-Allow-Origin”] = “*” 
response[“Access-Control-Allow-Methods”] = “POST, GET, OPTIONS” 
response[“Access-Control-Max-Age”] = “1000” 
response[“Access-Control-Allow-Headers”] = “*return response

五、基于原生django开发restful的接口

def books(request):
    # 获取所有图书
    if request.method=='GET':
        books=models.Book.objects.all()
        #把queryset对象转成json格式字符串
        # ll=[]
        # for book in books:
        #     bo={'name':book.name,'publish':book.publish}
        #     ll.append(bo)
        #列表推导式
        ll=[{'name':book.name,'publish':book.publish} for book in books]
        response={'code':100,'msg':'查询成功','data':ll}
        #safe=False 如果序列化的对象中有列表,需要设置
        return JsonResponse(response,safe=False,json_dumps_params={'ensure_ascii':False})

 六、drf安装、使用、APIView 的源码、equset的源码分析

DRF:Django REST framework

Django REST framework 框架是一个用于构建Web API 的强大而又灵活的工具。

通常简称为DRF框架 或 REST framework。

DRF框架是建立在Django框架基础之上,由Tom Christie大牛二次开发的开源项目。

特点

  • 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
  • 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
  • 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
  • 多种身份认证和权限认证方式的支持;
  • 内置了限流系统;
  • 直观的 API web 界面;
  • 可扩展性,插件丰富

安装、使用

-pip3 install djangorestframework
-pycharm中安装
-第一步,再写视图,都写cbv
from rest_framework.views import  APIView
class Books(APIView):
    pass
-在setting中配置 INSTALLED_APPS= [ 。。。。。 'rest_framework' ]

 源码分析:

APIView

1视图类继承了APIView之后,所有的请求都被禁用了csrf认证:
2.在APIView中as_view本质上还是调用了父类的as_view(View的as_view)

 3.as_view中调用dispatch,这个dispatch是APIView的dispatch

 

 APIView之dispatch方法:

-1、对原生的request对象做了一层包装(面向对象的封装),以后再用的request对象都是drf的新的request对象

-2、2 在APIView中self.initial(request, *args, **kwargs),里面有频率控制,权限控制和认证相关

-3 根据请求方法执行咱们写的视图类中的相应方法
--视图类中方法的request对象,已经变成了封装后的request

 APIView之Request方法:

-1 原生的request是self._request

-2 取以post形式提交的数据,从request.data中取(urlencoded,formdata,json格式)

-3 query_params 就是原生request的GET的数据

-4 上传的文件是从FILES中取
-5 (重点)其他的属性,直接request.属性名(因为重写了__getattr__方法)

 

 

 

posted @ 2019-07-02 19:43  MrSu  阅读(506)  评论(0编辑  收藏  举报