3月5日学习内容整理:restframework的规范,其中的认证功能

一、restful是什么

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

django的restframework框架就是做前后端分离开发的,就像是form组件,session等就是django为我们提供的一个功能组件

前后端分离开发,后端为前端提供URL,也可以说是API/接口开发,永远返回Httpresponse,前端发ajax请求提交获取数据

 

二、restful规范

与其说是规范,也就是建议,大家都这么开发

 

1、推荐使用https,更安全

2、域名的创建

》》》若用子域名的方式的话需要解决跨域的问题

》》》或者用相同的域名后面加上api:https://example.org/api/

3、版本

在URL中加入版本信息,以便区分,https://api.example.com/v1/

4、尽量使用名词,路径,视网络上任何东西都是资源,均使用名词表示(可复数)

5、要根据不同的请求method做出不同的操作

  • GET      :从服务器取出资源(一项或多项)
  • POST    :在服务器新建一个资源
  • PUT      :在服务器更新资源(客户端提供改变后的完整资源)
  • PATCH  :在服务器更新资源(客户端提供改变的属性)
  • DELETE :从服务器删除资源

6、过滤,利用URL中的数据传递查询条件

  • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量

7、状态码:

状态码是不能完全的表达响应信息的,通常我们都要在返回对象中加入code,从而能表示出更多的响应信息,给用户提供更多的提示信息

也就是状态码+code的形式

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

 

8、错误处理,状态码是4xx时,应返回错误信息,error当做key。

9、返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范

》》GET /collection:返回资源对象的列表(数组)

》》GET /collection/resource:返回单个资源对象

》》POST /collection:返回新生成的资源对象

》》PUT /collection/resource:返回完整的资源对象

》》PATCH /collection/resource:返回完整的资源对象

》》DELETE /collection/resource:返回一个空文档

 

10、返回对象中也尽可能的提供链接,指向别的API,用户使用起来更方便

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"
}}

 

 

 

三、restframework认证的源码流程

 1、怎么写:

from django.views import View
from rest_framework.views import APIView
from rest_framework.authentication import BasicAuthentication
from rest_framework import exceptions
from rest_framework.request import Request


class MyAuthentication(object):
    # 必须写这个方法
    def authenticate(self, request):
        token = request._request.GET.get('token')
        # 获取用户名和密码,去数据校验
        if not token:
            raise exceptions.AuthenticationFailed('用户认证失败')
        return ("alex", None)
    # 这个方法也必须要有
    def authenticate_header(self, val):
        pass


class DogView(APIView):
    authentication_classes = [MyAuthentication, ]

    def get(self, request, *args, **kwargs):
        print(request)
        print(request.user)
        ret = {
            'code': 1000,
            'msg': 'xxx'
        }
        return HttpResponse(json.dumps(ret), status=201)

    def post(self, request, *args, **kwargs):
        return HttpResponse('创建Dog')

    def put(self, request, *args, **kwargs):
        return HttpResponse('更新Dog')

    def delete(self, request, *args, **kwargs):
        return HttpResponse('删除Dog')    

 

2、具体的源码流程:

有三个类:自定义View类,以下简称自定义;APIView类(djangorestframework),以下简称API;View类(django),以下简称V

继承关系,自定义继承API,API继承V

注意,以下涉及到的属性和方法都要先从自定义去找,再从API找,再从V找,因为是自定义最先调用的as_view方法,也就是入口

1、URL路由匹配成功后首先执行as_view方法,实际上执行的是API的as_view,其中又用super方法执行V的as_view,再是执行V的view方法,再是执行dispatch方法,注意这里就会执行API中的dispatch方法如下第一图,

2、首先是将传入的原生request重新赋值,调用API的initialize_request方法,得到一个Request对象如下第二图,这个request封装了两个属性如下第三图,request._request指的就是原生的request,request.authenticators则是一个列表,里面是SessionAuthentication BasicAuthentication 这两个类的实例对象,这两个类是restframework里默认的,就是用来做认证的类,这两个类是API中的authentication_classes参数里定义的

3、将request重新封装后initialize_request函数结束后继续向下执行如下第一图,执行API的initial方法,在initial中执行self.perform_authentication(request)方法,在这个方法中运行request.user方法,这个request就是dispatch方法中重新封装的request也就是Request对象的user属性继而执行_authenticate方法,这个方法就会循环第2步中封装的request的authenticators属性,也就是存放着两个认证类的实例对象的列表,依次执行两个实例对象的authenticate方法,这个方法就是具体来做认证操作的

4、API的initial方法执行完后,也就代表认证通过,继续向下就会利用反射去执行不同请求method对应的函数了如下第一图

所以依据源码流程,我们自定义认证的话首先就要在自定义view类中定义好authentication_classes参数,不要去API中找,直接用自定义view类的,然后再自定义认证类,必须保证有authenticate方法,就在这个方法里做认证操作就可以了,一定要注意认证函数authenticate里的request已经是重新封装过的了,也就是Request对象

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        # 是Request的对象,self._request就是原生的request
        # self.authenticators就是是一个列表,里面是SessionAuthentication BasicAuthentication  这两个类的对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)7

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)

 

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(

            request,  # 这个request是原生的request
            parsers=self.get_parsers(),
            # 是一个列表,里面是SessionAuthentication BasicAuthentication  这两个类的对象
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

 

class Request(object):
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self._request = request
        self.authenticators = authenticators or ()

 

@property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user

 

    def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

 

posted @ 2018-03-05 17:17  九二零  阅读(205)  评论(0编辑  收藏  举报