聊聊restful和restframework

来聊聊这个RESTful

最近这段时间在看面试信息,很多公司的岗位要求中都有这个“掌握restful框架”。但是在我的认知里,restful好像是一种标准或者是风格啊,并不是某个具体的框架。因为自己也在这块有存疑。所以去网上寻找了一下答案。

看Url就知道要什么
看http method就知道干什么
看http status code就知道结果如何

如何理解这个REST

首先要知道全名:Representational State Transfer
逐字逐句的理解这三个单词:

  • Representational-------------表现层
    资源是一种信息实体,他可以有多种外在表现形式(例如文本可以是.txt.html.xml.json等多种格式)
    URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式(代表一个具体的html页面),属于"表现层"范畴,而URI(我们需要的真正的资源)应该只代表"资源"的位置。它(我们请求的资源)的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

  • state ---------状态
  • transfer----------转换
    互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

尽管我似乎明白了这个URI和状态转换的意义和意思,但是仍然不是“人话版本”。查询了知乎之后发现覃老师(覃超)已经在他的答案中引用了一个非常棒的解释

ivony:URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作

接下来就按照这个人话版本进行对restful的理解。

阮一峰api设计指南:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

如何设计

资源就是网络上的一个实体,或者说是网络上的一个具体信息,我们使用URI(统一资源定位符)指向他,每种资源对应一个特定的URI。所谓的上网,其实就是互联网上一系列的资源互动,调用他的URI

HTTP动词:

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

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

规范样例

  • 根地址:
https://example.org/api/v1/*
https://api.example.com/v1/*
  • 版本信息:可以放在URL里面,但是也可以用HTTP的header
/api/v1/
  • URL使用名词而不是动词,推荐使用复数形式:
GET /products : will return the list of all products

POST /products : will add a product to the collection

GET /products/4 : will retrieve product #4PATCH/

PUT /products/4 : will update product #4
  • 资源地址推荐使用嵌套结构
GET /friends/10375923/profile

UPDATE /profile/primaryAddress/city
  • 警惕返回结果的大小,设置分页和限制
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
  • 使用正确的HTTP status code表示访问状态:https://link.zhihu.com/?target=http%3A//www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
  • 在返回结果用明确易懂的文本,而且适当加入注释
  • 关于安全:自己的接口就用https,加上一个key做一次hash放在最后即可。考虑到国情,HTTPS在无线网络里不稳定,可以使用Application Level的加密手段把整个HTTP的payload加密。有兴趣的朋友可以用手机连上电脑的共享Wi-Fi,然后用Charles监听微信的网络请求(发照片或者刷朋友圈)。

注意事项:
API的身份认证应该使用OAuth 2.0框架。
服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

认证机制的坑

https://blog.igevin.info/posts/restful-architecture-in-general/#restful

顺带一提的Django rest framework

Django REST Framework是一个基于Django开发的APP,用于快速搭建REST API

安装

pip3 install djangorestframework

使用

注册app

settings.py

INSTALLED_APPS = [
    ...
    'rest_framework',
]

注册路由

from rest_framework import routers
from . import views


router = routers.DefaultRouter()
router.register(r'users', views.UserInfoViewSet)

urlpatterns = [
    url(r'^', include(router.urls)),
]

serializers,form验证以及数据库操作

from rest_framework import serializers
from . import models

class UserInfoSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.UserInfo
        # fields = ('id', 'username', 'pwd','ug') # fields = '__all__'
        exclude = ('ug',)
        depth = 1  # 0<=depth<=10

ViewSet视图函数

from rest_framework import viewsets
from . import models
from . import serializers

# ########### 1. 基本处理方式 ###########

class UserInfoViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    允许用户访问查看和编辑的终端路径API
    """
    queryset = models.UserInfo.objects.all().order_by('-id')
    serializer_class = serializers.UserInfoSerializer

基于CBV

url配置:

from django.conf.urls import url,include
from django.contrib import admin
from . import views

urlpatterns = [
    url(r'^users/$', views.UserList.as_view()),
    url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
]

视图函数编写:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser
from . import models
from . import serializers


class UserList(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all()
        serializer = serializers.MySerializer(instance=user_list, many=True)
        return Response(serializer.data)

    def post(self, request, *args, **kwargs):
        data = JSONParser().parse(request)
        serializer = serializers.MySerializer(data=data)
        if serializer.is_valid():
            # print(serializer.data)
            # print(serializer.errors)
            # print(serializer.validated_data)
            # 如果有instance,则执行update方法;否则,执行create
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)


class UserDetail(APIView):
    def get(self, request, *args, **kwargs):
        obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
        serializer = serializers.MySerializer(obj)
        return Response(serializer.data)

    def delete(self, request, *args, **kwargs):
        obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
        obj.delete()
        return Response(status=204)

    def put(self, request, *args, **kwargs):
        data = JSONParser().parse(request)
        obj = models.UserInfo.objects.filter(pk=kwargs.get('pk')).first()
        serializer = serializers.MySerializer(obj, data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=400)

编写seiralizers

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from . import models


class MySerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    username = serializers.CharField(required=False, allow_blank=True, max_length=100)
    pwd = serializers.CharField()

    def validate_username(self, value):
        if value == '中国':
            raise ValidationError('用户名中存在敏感字符')
        return value

    def validate_pwd(self, value):
        print(value)
        return value

    def validate(self, attrs):
        print(attrs)
        return attrs

    def create(self, validated_data):
        """
        当执行save方法时,自动调用。instance未传值
        :param validated_data:
        :return:
        """
        print(validated_data)
        return models.UserInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        当执行save方法时,自动调用。instance传值
        :param instance:
        :param validated_data:
        :return:
        """
        instance.username = validated_data.get('username', instance.username)
        instance.save()
        return instance

基于FBV

url

from django.conf.urls import url,include
from django.contrib import admin
from . import views

urlpatterns = [
    url(r'^users/$', views.user_list),
    url(r'^users/(?P<pk>[0-9]+)/$', views.user_detail),
]

视图:


from django.http import JsonResponse,HttpResponse
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
from rest_framework.decorators import api_view
from .serializers import MySerializer
from . import models

@api_view(['GET',"POST"])
def user_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        user_list = models.UserInfo.objects.all()
        serializer = MySerializer(user_list,many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = MySerializer(data=data)
        if serializer.is_valid():
            print(serializer.data)
            print(serializer.errors)
            print(serializer.validated_data)
            # 如果有instance,则执行update方法;否则,执行create
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)

@api_view(['GET',"POST","PUT"])
def user_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    """

    obj = models.UserInfo.objects.filter(pk=pk).first()
    if not obj:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = MySerializer(obj)
        # return JsonResponse(serializer.data,json_dumps_params={'ensure_ascii':False},content_type='application/json;charset=utf-8')
        return Response(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = MySerializer(obj, data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=400)

    elif request.method == 'DELETE':
        obj.delete()
        return Response(status=204)

编写serializers

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from . import models


class MySerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    username = serializers.CharField(required=False, allow_blank=True, max_length=100)
    pwd = serializers.CharField()

    def validate_username(self, value):
        if value == '中国':
            raise ValidationError('用户名中存在敏感字符')
        return value

    def validate_pwd(self, value):
        print(value)
        return value

    def validate(self, attrs):
        print(attrs)
        return attrs

    def create(self, validated_data):
        """
        当执行save方法时,自动调用。instance未传值
        :param validated_data:
        :return:
        """
        print(validated_data)
        return models.UserInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        当执行save方法时,自动调用。instance传值
        :param instance:
        :param validated_data:
        :return:
        """
        instance.username = validated_data.get('username', instance.username)
        instance.save()
        return instance

权限控制

settings.py配置

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'permissi.MyPermission',
    ]
}

权限控制:


class MyPermission(object):
    """
    A base class from which all permission classes should inherit.
    """

    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """

        return True

    def has_object_permission(self, request, view, obj):
        return True

转载于:https://www.cnblogs.com/scott-lv/p/7728855.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值