在开发web应用中,有两种应用模式:前后端不分离和前后端分离。
前后端不分离
前后端分离
前后端分离开发示例
获取图书数据 Web API
- 请求方式:get
- 请求路径:book/1
- 请求参数:图书id 1
- 请求结果:json {‘books’:[{‘name’:‘西游记’,‘bread’:20},{‘name’:‘三国’,‘bread’:30}]}
前端
<ul>
<li v-for="book in data">
{
{
book.name}}
{
{
book.bread}}
</li>
</ul>
axios.get('http:\\127.0.0.1:8000/book/1')
# 200系列状态码,执行then
.then(response => {
this.data = response.data.books
})
# 返回400/500状态码,执行catch
.catch(response = >{
response.data.error
})
后端
url(r'book/(?P\d+)' ,views.BookView.as_view())
class BookView(View):
def get(self,request,pk):
# 1.获取数据
# 2.查询数据
try:
book = Book.object.get(id=pk)
except:
return HttpResponse()
data = {
"name":book.name, "bread":book.bread}
return JsonResponse(data)
在前后端分离的应用模式里,API接口普遍采用RESTful设计风格
1.域名:应该尽量将API部署在专有域名之下
https://api.example.com
如京东:https://shouji.jd.com/
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下
https:example.org/api/
2.版本:应该将API的版本号放入url
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
另一种做法是将版本号放在HTTP头信息中,因为不同的版本可以理解成同一种资源的不同表现形式,所以应该采用同一种url,版本号可以放在HTTP请求头信息的Accept字段中进行区分。
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
3.路径
路径又称“终点”,表示API的具体网址,每个网址代表一种资源。
4.HTTP动词
对于资源的具体操作类型,由HTTP动词表示,常用的HTTP动词有下面四个。
GET:从服务器取出资源(一项或多项)。
POST:在服务器新建一个资源。
PUT:在服务器更新资源(客户端提供改变后的完整资源)。
DELETE:从服务器删除资源。
如:
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:删除某个指定动物园的指定动物
5.过滤信息以查询字符串进行
如果记录数量很多,API应该提供参数,过滤返回结果。
下面是一些常见的参数:
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定
6.状态码
后端执行成功或失败以状态码的形式告诉前端,处理成功返回200系列状态码,执行前端then里面的代码;处理失败返回400/500系列状态码,执行catch里面的代码。
- 200 OK :服务器成功返回用户请求的数据
- 201 CREATED :用户新建或修改数据成功。
- 202 Accepted :表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT :用户删除数据成功。
- 400 INVALID REQUEST :用户发出的请求有错误,服务器没有进行新建或修改数据的操作
- 401 Unauthorized :表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden :表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND :用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable :用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity : 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR :服务器发生错误,用户将无法判断发出的请求是否成功
{
error:"出错信息"
}
使用图书英雄案例来写一套支持图书数据增删改查的REST API接口
① 接口设计
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
POST | books/ | title、bpub_date (请求体json) | 保存后的图书数据books=[{btitle:“西游记”,bpub_date:2017},{…}] |
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
DELETE | books/1 | id (路径) |
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
PUT | books/1 | 修改字段内容(json)、id (路径) | 更新后的图书数据 book={ btitle:“xxx”,bpub_date:“xxx”} |
查询单一图书
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
GET | books/1 | id (路径) | book={ btitle:“xxx”,bpub_date:“xxx”} |
查询所有图书
请求方式 | 请求路径 | 请求参数 | 返回结果 |
---|---|---|---|
GET | books/1 | books=[{btitle:“西游记”,bpub_date:2017},{…}] |
② 后端代码实现
urlpatterns = [
url(r'^books/$', views.BooksView.as_view()),
url(r'^books/(?P\d+)/' , views.BookView.as_view()),
]
from django.views import View
from django.http import JsonResponse
from book.models import BookInfo
import json
class BooksView(View):
def get(self,request):
"""获取所有图书""""
books = BookInfo.objects.all()
data = []
for book in books:
data.append({
"id": book.id,
"btitle": book.btitle,
"bpub_date": book.bpub_date
})
# safe=False,是将列表[]转成json中的数组"[]"
return JsonResponse(data, safe=False)
def post(self,request):
"""保存图书"""
# 1、获取前端数据
data = request.body.decode()
data_dict = json.loads(data)
btitle = data_dict.get("btitle")
bpub_date = data_dict.get("bpub_date")
# 2、验证数据
if btitle == "python":
return JsonResponse({
"error": "错误的书名"},status=400)
# 3、保存数据
book = BookInfo.objects.create(btitle=btitle, bpub_date=bpub_date)
# 4、返回结果
return JsonResponse({
"id": book.id,
"bpub_date": book.bpub_date,
"btitle": book.btitle
})
class BookView(View):
def get(self,request,pk):
"""查询一本图书"""
try:
book = BookInfo.objects.get(id=pk)
except:
return JsonResponse({
"id": book.id,
"btitle": book.btitle,
"bpub_date": book.bpub_date
})
def put(self,request,pk):
"""修改一本图书"""
# 1、获取数据
data = request.body.decode()
data_dict = json.loads(data)
btitle = data_dict.get("btitle")
bpub_date = data_dict.get("bpub_date")
# 2、校验数据
# 3、更新数据
# 这种方法得到的并不是对象,而是得到更新数据的个数
# book = BookInfo.objects.filter(id=pk).update(btitle=btitle,bpub_date=bpub_date)
try:
book = BookInfo.objects.get(id=pk)
except:
return JsonResponse({
"error": "错误的id"},status=400)
book.btitle = btitle
book.bpub_date = bpub_date
book.save()
# 4、返回结果
return JsonResponse({
"id": book.id,
"btitle": book.btitle,
"bpub_date": book.bpub_date
})
def delete(self,request,pk):
"""删除一本图书"""
try:
book = BookInfo.objects.get(id=pk)
except:
return JsonResponse({
"error": "错误的id"}, status=400)
book.delete()
pip install djangorestframework
INSTALLED_APPS = [
...
'rest_framework',
]
序列化器的作用:
在应用的目录下创建serializers.py文件,Serializer使用类来定义,须继承自rest_framework.serializers.Serializer.
from rest_framework import serializers
class BookSerializer(serializers.Serializers):
# 根据模型类字段进行定义字段
btitle=serializers.CharField()
bpub_date=serializers.DateField()
注:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义,serializer是独立于数据库之外的存在。
from book.serializers import BookSerializer
class BookView(View):
def get(self,request,pk):
"""查询一本图书"""
try:
# 查询出一个图书对象
book = BookInfo.objects.get(id=pk)
except:
# return JsonResponse({"id": book.id,"btitle": book.btitle,"bpub_date": book.bpub_date})
# 1.构造序列化器对象
serializer = BookSerializer(book)
# 2.通过data方法获取序列化数据
data = serializer.data
return JsonResponse(data)
from book.serializers import BookSerializer
class BooksView(View):
def get(self,request):
"""获取所有图书""""
books = BookInfo.objects.all()
# data = []
# for book in books:
# data.append({"id": book.id,"btitle": book.btitle,"bpub_date": book.bpub_date})
serializer = BookSerializer(books,many=True)
return JsonResponse(serializer.data, safe=False)
heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True,many=True)
{
"btitle":"天龙八部", "bpub_date": "1986-07-24", "heroinfo_set":[6,7,8,9]}