zoukankan      html  css  js  c++  java
  • RESTful API

    什么是RESTful:

      REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer 的简称,中文翻译为“表征状态转移”或“表现层状态转化”。

    RESTful API设计:

    1,API与用户的通信协议:

          总是使用HTTPs协议。

    2,域名   

        https://api.example.com                         尽量将API部署在专用域名

        https://example.org/api/                        API很简单

    3,版本

        1.  将版本信息放在URL中,如:https://api.example.com/v1/

        2. 将版本信息放在请求头中。

    4,路径

        网络上任何东西都是资源,均使用名字表示(可复数)    

        https://api.example.com/v1/zoos

        https://api.example.com/v1/animals

        https://api.example.com/v1/employees

    5,method

        GET:从服务器取出资源(一项或多项)    

        POST    :在服务器新建一个资源

        PUT      :在服务器更新资源(客户端提供改变后的完整资源)

        PATCH  :在服务器更新资源(客户端提供改变的属性)

        DELETE :从服务器删除资源

    6,过滤: 

      通过在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:指定筛选条件

    7,状态码:

    OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    NO CONTENT - [DELETE]:用户删除数据成功。
    INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

    错误处理:

      状态码是4xx时,应返回错误信息,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"
    }}

    基于Django REST Framework实现RESTful API:

    models:

    from django.db import models
    
    # Create your models here.
    
    
    class Article(models.Model):
        # title = models.CharField(max_length=32,unique=True,error_messages={"unique":"文章标题不能重复"})
        title = models.CharField(max_length=32,unique=True)
        # 文章发布时间
        create_time = models.DateField(auto_now_add=True)
        # auto_now_add = True 把第一次创建的时间保存
        # auto_now = True 每次更新的时候就会把该字段值保存为当前时间
        type = models.SmallIntegerField(
            choices=((1,"原创"),(2,"转载")),
            default=1
        )
        # school 关联的外键
        school = models.ForeignKey(to="School",on_delete=models.CASCADE)
        # tag 关联标签外键
        tag = models.ManyToManyField(to="Tag")
    
    
    class School(models.Model):
        name = models.CharField(max_length=32)
    
    
    class Tag(models.Model):
        name = models.CharField(max_length=32)
    
    
    class Comment(models.Model):
        content = models.CharField(max_length=128)
        article = models.ForeignKey(to="Article",on_delete=models.CASCADE)

    url:

    from django.conf.urls import url,include
    from django.contrib import admin
    from app01 import app01_urls
    
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/', include(app01_urls)),
    ]

    app01_urls:

    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r"comment/",views.Comment.as_view()),
        url(r"article/$",views.Article.as_view()),
        url(r"school/(?P<id>d+)",views.SchoolDetail.as_view(),name="school-detail"),
        url(r"article/(?P<pk>d+)",views.ArticleDetail.as_view(),name="article-detail"),
    
    ]

    views:

    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    from app01 import models
    from rest_framework import serializers
    from django import views
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app01 import serializers as app01_serializers
    
    # Create your views here.
    
    
    # 评论CBV
    class Comment(APIView):
    
        def get(self, request):
            # print(request)
            res = {"code":1}
            print(self.request)  # rest framework 封装好的request
            print(self.request.data)   # 此时的request则是封装好后的
            print(self.request._request.GET.get("age"))  # self.request._request 则是Django原来的request对象
            # 数据
            all_comment = models.Comment.objects.all()
            # 序列化
            ser_obj = app01_serializers.CommentSerializer(all_comment,many=True)
            res["data"] = ser_obj.data
            return Response(res)
    
        def post(self,request):
            res = {"code":0}
            # 去提交的数据
            comment_data = self.request.data
            print(comment_data)   # <QueryDict: {'age': ['17']}> 获取的数据是QueryDict类型
            # 对用户提交的的数据做校验
            ser_obj = app01_serializers.CommentSerializer(data=comment_data)
            if ser_obj.is_valid():
                # 表示数据没有问题
                ser_obj.save()
                # 创建数据
            else:
                # 表示数据有问题
                res["code"] = 0
                res["data"] = ser_obj.errors   # 将错误的提示信息传给res
            return Response(res)
    
        def put(self,request):
            print(self.request)
            print(self.request.data)
            return HttpResponse("修改评论")
    
        def delete(self,request):
            print(self.request)
            print(self.request.data)
            return HttpResponse("删除评论")
    
    
    # 文章CBV
    class Article(APIView):
        def get(self,request):
            res = {"code":1}
            all_article = models.Article.objects.all()
            ser_obj = app01_serializers.ArticleHyperLinkedSerializer(all_article,many=True,context={"request":request})
            # ser_obj = app01_serializers.ArticleHyperLinkedSerializer(all_article,many=True)
            res["data"] = ser_obj.data
            print(ser_obj.data)
            return Response(res)
    
        def post(self,request):
            res = {"code":1}
            ser_obj = app01_serializers.ArticleModelSerializer(data=self.request.data)
            # 通过文章序列化数据得到 对象
            if ser_obj.is_valid():
                ser_obj.save()   # 确认数据符合要求保存
            else:
                res["code"] = 0
                res["data"] = ser_obj.errors
            return Response(res)
    
    
    # 文章详情CBV
    class ArticleDetail(APIView):
        def get(self,request,pk):
            print(request)
            print(pk)
            res = {"code":1}
            article_obj = models.Article.objects.filter(pk=pk).first()
            # 序列化
            ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj,context={"request":request})
            # ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj)
            print(ser_obj.data)
            res["data"] = ser_obj.data
            return Response(res)
    
    class SchoolDetail(APIView):
        def get(self,request,id):
            res = {"code":1}
            school_obj = models.School.objects.filter(pk=id).first()
            print(111)
            ser_obj = app01_serializers.SchoolSerializer(school_obj,context={"request":request})
            # ser_obj = app01_serializers.SchoolSerializer(school_obj)
            res["data"] = ser_obj.data
            return Response(res)

    serializers:

    from app01.models import Comment,Article,School
    from rest_framework import serializers
    from rest_framework.validators import ValidationError
    
    # 序列化评论的类
    class CommentSerializer(serializers.ModelSerializer):
        # content = serializers.CharField(error_messages={"required":"内容不能为空"})
    
    
        # article_detail = serializers.SerializerMethodField(read_only=True)
        # 在序列化类中自定义的字段:SerializerMethodField,在类中自定义字段。get_自定义字段名(self,obj)方法。
        #  read_only : 只在查询时才会显示相应的字段。
        # def get_article_detail(self,obj):
        #     print(obj.article.title)
        #    返回的值当作新字段的值
        #     return obj.article.title
    
        # 与forms组件的钩子函数一致,使用 validata_字段名 构成钩子函数。
        def validate_content(self,value):
            if "somethings" in value:
                # 内容不符合要求,则直接终止程序抛出错误。
                raise ValidationError("内容不合法")
            else:
                # 内容符合要求,则需直接返回原值即刻
                return value
    
    
        # 全局钩子
        def validate(self,value):
            # somethins code
            pass
    
        class Meta:
            model = Comment
            fields = "__all__"
            depth = 1
    
            # 定义额外的参数
            extra_kwargs = {
                "content": {
                    "error_messages": {
                        "required": '内容不能为空',
                    }
                },
                "article": {
                    "error_messages": {
                        "required": '文章不能为空'
                    }
                }
            }
    
    
    # 文章的序列化类
    class ArticleModelSerializer(serializers.ModelSerializer):
        # type = serializers.CharField(source="get_type_display")
        class Meta:
            model = Article   # 绑定ORM类是哪一个
            fields = "__all__"
            # depth = 1
    
            # 定义额外的参数
            extra_kwargs = {
    
                "type": {
                    "error_messages": {
                        "required": "类型不能为空"
                    }
                },
                "title": {
                    "error_messages": {
                        "required": "标题不能为空"
                    }
                },
            }
    
    
    # 文章超链接序列化
    class ArticleHyperLinkedSerializer(serializers.HyperlinkedModelSerializer):
        school = serializers.HyperlinkedIdentityField(view_name="school-detail",lookup_url_kwarg="id")
        type = serializers.CharField(source="get_type_display")
        # lookup_url_kwarg : 期望寻找的url参数是 id(路径传过来的参数值).
        # view_name:若是不写这个属性,则默认寻找的是url对应的别名:school-detail
                    # 若是写则需要值对应别名的值。
        class Meta:
            model = Article
            fields = ["id","title","type","school",]
    
    
    # 学校的序列化
    class SchoolSerializer(serializers.ModelSerializer):
        class Meta:
            model = School
            fields = "__all__"

    超链接的序列化:

      1,超链接字段的三个参数:

        view_name: 默认使用  表名-detail  ,可以设置为路径的别名。

        lookup_url_kwarg: 默认使用pk,指的是反向生成URL时,路由中分组命名匹配的key.

        lookpu_field : 默认使用的pk,指的是反向生成URL的时候,路由中分组命名匹配的value.

      2,想要生成完整的超链接API,在序列化的时候要传入参数:context:{"request":request}.

       上面生成的是绝对路径,若是想生成相对路径:context:{"request":None }

  • 相关阅读:
    idea创建Javaweb项目
    装配bean,基于xml
    IoC(控制反转)和DI(依赖注入)
    jar包介绍
    使用的基本方法
    struts2的常量
    jar包
    调用函数,模块
    Vue中的指令(听博主说总结的很好)
    React的核心概念
  • 原文地址:https://www.cnblogs.com/stfei/p/9404195.html
Copyright © 2011-2022 走看看