DjangoRestFrameWork之从基础到深入

1.首先从RestFul说起

认识RESTful

RESTful API
实用的是如何正确地理解 RESTful架构和设计好RESTful API。

首先为什么要用RESTful结构呢?
大家都知道"古代"网页是前端后端融在一起的,比如之前的PHP,JSP等。在之前的桌面时代问题不大,但是近年来移动互联网的发展,各种类型的Client层出不穷,RESTful可以通过一套统一的接口为 Web,iOS和Android提供服务。另外对于广大平台来说,比如Facebook platform,微博开放平台,微信公共平台等,它们不需要有显式的前端,只需要一套提供服务的接口,于是RESTful更是它们最好的选择。在RESTful架构下

在前后端分离的应用模式里,后端API接口如何定义?

例如对于后端数据库中保存了商品的信息,前端可能需要对商品数据进行增删改查,那相应的每个操作后端都需要提供一个API接口:

  1. POST /add-goods 增加商品
  2. POST /delete-goods 删除商品
  3. POST /update-goods 修改商品
  4. GET /get-goods 查询商品信息

对于接口的请求方式与路径,每个后端开发人员可能都有自己的定义方式,风格迥异。

是否存在一种统一的定义方式,被广大开发人员接受认可的方式呢?

这就是被普遍采用的API的RESTful设计风格。

2. 名称

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。维基百科称其为“具象状态传输”,国内大部分人理解为“表现层状态转化”。

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

3. 总结

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

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

RESTful设计方法总结起来,最终的几点设计方法

1. 路径(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource)

(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。

举例来说,以下是不好的例子:

/getProducts
/listOrders
/retreiveClientByOrder?orderId=1

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

GET /products :将返回所有产品清单
POST /products :将产品新建到集合
GET /products/4 :将获取产品 4
PATCH(或)PUT /products/4 :将更新产品 4

(2) API中的名词应该使用复数。无论子资源或者所有资源。

举例来说,获取产品的API可以这样定义

获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products

2. HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

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

还有三个不常用的HTTP动词。

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。


GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

关注读取接口	  friendships/friends	
获取用户的关注列表  friendships/friends/ids	
粉丝读取接口	friendships/followers
获取用户粉丝列表  friendships/followers/ids
关系读取接口	friendships/show	

3. 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoos/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

4. 状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 200 OK - [GET]:服务器成功返回用户请求的数据
  • 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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

接下来我们使用Django来开发一套REST 接口

import json

from django.http import HttpResponse
from django.views import View
from django.http import JsonResponse

from booktest.models import BookInfo
# Create your views here.

# Django基础知识自定义RestAPI接口
# 1. 获取所有图书的数据	 GET /books/
# 2. 新增一本图书数据	 POST /books/
# 3. 获取指定的图书数据	 GET  /books/ID/
# 4. 修改指定的图书数据	 PUT  /books/ID/
# 5. 删除指定的图书数据	 DELETE /books/ID/


# /books/
class BookListView(View):
    def get(self, request):
        """获取所有图书的数据"""
        # 获取所有图书的数据
        books = BookInfo.objects.all()

        # 将books中每个图书对象转换为字典
        books_li = []

        for book in books:
            book_dict = {
                'id': book.id,
                'btitle': book.btitle,
                'bpub_date': book.bpub_date,
                'bread': book.bread,
                'bcomment': book.bcomment,
                'image': book.image.url if book.image else ''
            }

            books_li.append(book_dict)

        # 返回图书列表的json数据,状态码: 200
        return JsonResponse(books_li, safe=False)

    def post(self, request):
        """新增一本图书数据"""
        # 需求: 让客户端通过json数据传递新增图书参数(btitle, bpub_date)

        # 获取请求体中原始数据
        req_data = request.body # bytes
        # 将bytes转换为str
        json_str = req_data.decode()
        # 将json字符串转换为python字典
        req_dict = json.loads(json_str)

        # 获取数据并进行校验
        btitle = req_dict.get('btitle')
        bpub_date = req_dict.get('bpub_date')

        # TODO: 省略参数校验的过程...

        # 创建并新增图书的数据
        book = BookInfo.objects.create(
            btitle=btitle,
            bpub_date=bpub_date
        )

        # 返回响应: 状态码 201 新增图书的数据
        book_dict = {
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
            'image': book.image.url if book.image else ''
        }

        return JsonResponse(book_dict, status=201)


# /books/ID/
# /books/(?P<pk>\d+)/
# /books/100/
class BookDetailView(View):
    def get(self, request, pk):
        """获取指定的图书数据"""
        # 根据pk获取对应的图书
        try:
            book = BookInfo.objects.get(pk=pk)
        except BookInfo.DoesNotExist:
            # 图书不存在
            return HttpResponse(status=404)

        # 返回应答: 状态码 200 对应图书数据
        book_dict = {
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
            'image': book.image.url if book.image else ''
        }

        return JsonResponse(book_dict)

    def put(self, request, pk):
        """修改指定的图书数据"""
        # 需求: 让客户端通过json数据传递修改图书参数(btitle, bpub_date)

        # 根据pk获取对应图书
        try:
            book = BookInfo.objects.get(pk=pk)
        except BookInfo.DoesNotExist:
            # 图书不存在
            return HttpResponse(status=404)

        # 接收数据并进行参数校验
        # 获取请求体中原始数据
        req_data = request.body  # bytes
        # 将bytes转换为str
        json_str = req_data.decode()
        # 将json字符串转换为python字典
        req_dict = json.loads(json_str)

        # 获取数据并进行校验
        btitle = req_dict.get('btitle')
        bpub_date = req_dict.get('bpub_date')

        # TODO: 省略参数校验的过程...

        # 修改对应的图书数据
        book.btitle = btitle
        book.bpub_date = bpub_date
        book.save()

        # 返回应答: 状态码 200 修改图书数据
        book_dict = {
            'id': book.id,
            'btitle': book.btitle,
            'bpub_date': book.bpub_date,
            'bread': book.bread,
            'bcomment': book.bcomment,
            'image': book.image.url if book.image else ''
        }

        return JsonResponse(book_dict)

    def delete(self, request, pk):
        """删除指定的图书数据"""
        # 根据pk获取对应图书
        try:
            book = BookInfo.objects.get(pk=pk)
        except BookInfo.DoesNotExist:
            # 图书不存在
            return HttpResponse(status=404)

        # 删除图书
        book.delete()

        # 返回应答: 状态码 204 空
        return HttpResponse(status=204)


我们可以明确,restful-api开发的流程与主要任务

可以发现,在开发REST API接口时,视图中做的最主要有三件事:

  • 将请求的数据(如JSON格式)转换为模型类对象
  • 操作数据库
  • 将模型类对象转换为响应的数据(如JSON格式)

下一次会分享使用DRF(DjangoRestFrameWork)的序列化器来开发一套RestAPI接口

 

猜你喜欢

转载自blog.csdn.net/qq_42514453/article/details/84889223
今日推荐