文章目錄
【拍出不一樣的學校】航拍中央財經大學
這是我的學校——財經黃埔,你想來嗎o( ̄▽ ̄)ブ
一、Restful API規範
Restful API是用於在前端與後臺進行通信的一套規範,使用這個規範可以讓前後端開發變得更加簡單。
1.協議
採用http或者https協議。
2.數據傳輸格式
數據之間傳輸的格式應該使用json形式,而不是xml。
3.url鏈接
url鏈接中不能有動詞,只能有名詞;
對於一些名詞,如果出現複數,應該在後面加s。
4.HTTP請求方法
方法 | 含義 | 舉例 |
---|---|---|
GET | 從服務器上獲取資源 | /users/ 獲取所有用戶 |
… | … | /user/id/ 根據id獲取一個用戶 |
POST | 在服務器上新創建一個資源 | /user/ 新建一個用戶 |
PUT | 在服務器上更新資源(客戶端提供所有改變後的數據) | /user/id/ 更新某個id的用戶的信息(需要提供用戶的所有信息) |
PATCH | 在服務器上更新資源(客戶端只提供需要改變的屬性) | /user/id/ 更新某個id的用戶信息(只需要提供需要改變的信息) |
DELETE | 從服務器上刪除資源 | /user/id/ 刪除一個用戶 |
狀態碼
狀態碼 | 描述 | 含義 |
---|---|---|
200 | OK | 服務器成功響應客戶端的請求 |
400 | INVALID REQUEST | 用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作 |
401 | Unauthorized | 用戶沒有權限訪問這個請求 |
403 | Forbidden | 因爲某些原因禁止訪問這個請求 |
404 | NOT FOUND | 用戶發送的請求的url不存在 |
406 | NOT Acceptable | 用戶請求不被服務器接收(比如服務器期望客戶端發送某個字段,但是沒有發送) |
500 | Internal server error | 服務器內部錯誤,比如出現了bug |
二、Flask-Restful插件的基本使用
1.基本概念
Flask-Restful是Flask中專門用來實現Restful API的插件,使用它可以快速集成Restful API功能。
在app的後臺以及純API的後臺中,這個插件可以幫助我們節省很多時間;
在普通的網站中,這個插件顯得有些雞肋,因爲在普通的網頁開發中,是需要去渲染HTML代碼的,而Flask-Restful在每個請求中都返回json格式的數據。
2.安裝
Flask-Restful的環境要求:
- Flask版本>=0.8
- Python版本爲2.6、2.7,或3.3及以上。
在虛擬環境中使用pip install flask-restful
命令安裝。
3.基本使用
使用Flask-Restful定義視圖類時,要繼承自flask_restful.Resource
類,再根據具體的請求的方法來定義相應的方法。
例如,如果期望用戶在客戶端中使用get方法發送請求,那麼就定義一個get方法,類似於MethodView。
簡單使用測試如下:
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
class IndexView(Resource):
def get(self):
return {'username': 'Corley'}
def post(self):
return {'info': 'Login Successfully!!'}
api.add_resource(IndexView, '/', endpoint='index')
if __name__ == '__main__':
app.run(debug=True)
運行之後,使用Postman模擬請求如下:
顯然,用get方法和post方法請求得到的結果是不一樣的。
說明:
- endpoint用來指定使用
url_for()
方法反轉url時傳入的參數;如果不設置endpoint參數,會使用視圖的名字的小寫作爲endpoint的值。 add_resource()
方法的第二個參數是視圖函數的路由地址,這個地址與視圖函數的route一樣,可以傳遞參數;有一點不同的是,這個方法可以傳遞多個url來指定視圖函數。
三、Restful參數解析
Flask-Restful插件提供了類似WTForms來驗證提交的數據是否合法的庫,即reqparse
。
RequestParser對象的add_argument()
方法可以指定字段的名字、數據類型等,常見的參數有:
- default
默認值,如果參數沒有值,那麼將使用這個參數指定的值。 - required
是否必須。
默認爲False;如果設置爲True,則必須提交這個參數。 - type
參數的數據類型,如果指定,那麼將使用指定的數據類型來強制轉換提交得到的值。 - choices
選項,提交上來的值只有滿足這個選項中的值才符合驗證通過,否則驗證不通過。 - help
錯誤信息,如果驗證失敗後,將會使用這個參數指定的值作爲錯誤信息。 - trim
是否要去掉前後的空格。
練習如下:
from flask import Flask
from flask_restful import Api, Resource, reqparse, inputs
app = Flask(__name__)
api = Api(app)
class IndexView(Resource):
def get(self):
return {'username': 'Corley'}
def post(self):
parse = reqparse.RequestParser()
parse.add_argument('username', type=str, help='用戶名驗證錯誤', required=True)
parse.add_argument('password', type=str, help='密碼驗證錯誤', trim=True)
parse.add_argument('age', type=int, help='年齡錯誤')
parse.add_argument('gender', type=str, help='性別錯誤', choices=['male', 'female', 'secret'], default='secret')
parse.add_argument('blog', type=inputs.url, help='博客地址錯誤')
parse.add_argument('phone', type=inputs.regex(r'1[35789]\d{9}'), help='電話號碼錯誤')
args = parse.parse_args()
return {'info': 'Register Successfully!!', 'args': args}
api.add_resource(IndexView, '/', endpoint='index')
if __name__ == '__main__':
app.run(debug=True)
顯示:
四、Restful高級使用
1.輸出字段
對於一個視圖類,可以預設好一些字段用於返回,以後使用ORM模型或者自定義模型的時候,會自動獲取模型中相應的字段,生成json數據,再返回給客戶端。
這需要導入flask_restful.marshal_with
裝飾器,在視圖類中寫一個字典,來指示需要返回的字段,以及該字段的數據類型。
測試如下:
from flask import Flask
from flask_restful import Api, Resource, reqparse, inputs, fields, marshal_with
app = Flask(__name__)
api = Api(app)
class IndexView(Resource):
def get(self):
return {'username': 'Corley'}
def post(self):
parse = reqparse.RequestParser()
parse.add_argument('username', type=str, help='用戶名驗證錯誤', required=True)
parse.add_argument('password', type=str, help='密碼驗證錯誤', trim=True)
parse.add_argument('age', type=int, help='年齡錯誤')
parse.add_argument('gender', type=str, help='性別錯誤', choices=['male', 'female', 'secret'], default='secret')
parse.add_argument('blog', type=inputs.url, help='博客地址錯誤')
parse.add_argument('phone', type=inputs.regex(r'1[35789]\d{9}'), help='電話號碼錯誤')
args = parse.parse_args()
return {'info': 'Register Successfully!!', 'args': args}
class ArticleView(Resource):
resource_fields = {
'title': fields.String,
'content': fields.String
}
@marshal_with(resource_fields)
def get(self):
return {'title': 'Python Flask'}
api.add_resource(IndexView, '/', endpoint='index')
api.add_resource(ArticleView, '/article/', endpoint='article')
if __name__ == '__main__':
app.run(debug=True)
顯示:
顯然,get方法使用了marshal_with
裝飾器後,即使返回時未定義content字段,在網頁中也會渲染該字段。
還可以在get方法中直接返回數據庫的查詢結果,示例如下:
class ArticleView(Resource):
resource_fields = {
'title': fields.String,
'content': fields.String,
'tags': fields.String
}
@marshal_with(resource_fields)
def get(self, article_id):
article = Article.query.get(article_id)
return article
在get方法中返回article的時候,flask_restful會自動讀取Article模型上的title、content和tags屬性,組裝成json格式的字符串返回給客戶端。
2.複雜結構與格式化輸出
有時候想要在返回的數據格式中,形成複雜的結構、美觀的格式,此時可以使用一些特殊的字段來實現。
要在一個字段中放置一個列表,可以使用fields.List
;
在一個字段下面又是一個字典,可以使用fields.Nested
。
練習如下:
在當前項目目錄下創建主程序文件flask_restful_test.py如下:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
import config
from exts import db
from models import Article
app = Flask(__name__)
api = Api(app)
app.config.from_object(config)
db.init_app(app)
@app.route('/')
def index():
return '這是首頁'
class ArticleView(Resource):
resource_fields = {
'title': fields.String,
'content': fields.String,
'author': fields.Nested({
'username': fields.String,
'email': fields.String
}),
'tags': fields.List({
'id': fields.Integer,
'name': fields.String
})
}
@marshal_with(resource_fields)
def get(self, article_id):
article = Article.query.get(article_id=article_id)
return article
api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')
if __name__ == '__main__':
app.run(debug=True)
創建配置文件config.py如下:
# 數據庫連接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_restful'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URL
SQLALCHEMY_TRACK_MODIFICATIONS = False
創建模型文件models.py如下:
from exts import db
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(30))
email = db.Column(db.String(50))
article_tag_table = db.Table(
'article_tag',
db.Column('article_id', db.Integer, db.ForeignKey('article.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)
class Article(db.Model):
__tablename__ = 'article'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(30))
content = db.Column(db.String(500))
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
author = db.relationship('User', backref='articles')
tags = db.relationship('Tag', secondary=article_tag_table, backref='tags')
class Tag(db.Model):
__tablename__ = 'tag'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(30))
創建中間文件exts.py如下:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
創建數據庫遷移映射文件manage.py如下:
from flask_script import Manager
from flask_restful_test import app
from flask_migrate import Migrate, MigrateCommand
from exts import db
import models
manager = Manager(app)
Migrate(app, db)
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()
在命令行當前目錄下依次執行python manage.py db init
、python manage.py db migrate
、python manage.py db upgrade
,執行成功後可以在flask_restful數據庫中看到建立了四個表,爲了方便,手動插入數據如下:
mysql> select * from user;
+----+----------+-------------+
| id | username | email |
+----+----------+-------------+
| 1 | Corley | 123@163.com |
+----+----------+-------------+
1 row in set (0.00 sec)
mysql> select * from article;
+----+--------------+------------------+-----------+
| id | title | content | author_id |
+----+--------------+------------------+-----------+
| 1 | Python Flask | Migrate, Restful | 1 |
+----+--------------+------------------+-----------+
1 row in set (0.00 sec)
mysql> select * from tag;
+----+-------------+
| id | name |
+----+-------------+
| 1 | Python |
| 2 | Programming |
+----+-------------+
2 rows in set (0.00 sec)
mysql> select * from article_tag;
+------------+--------+
| article_id | tag_id |
+------------+--------+
| 1 | 1 |
| 1 | 2 |
+------------+--------+
2 rows in set (0.01 sec)
運行主程序後,訪問http://127.0.0.1:5000/article/1,可以看到:
顯然, 得到了美化的json格式的數據。
3.重命名屬性
很多時候面向用戶的字段名稱是不同於內部的屬性名,此時使用attribute
參數可以配置這種映射。
比如現在想要返回article的id,但是想返回article_id
,可以寫成'article_title': fields.String(attribute='title')
。
主程序文件中屬性重命名示例如下:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
import config
from exts import db
from models import Article
app = Flask(__name__)
api = Api(app)
app.config.from_object(config)
db.init_app(app)
@app.route('/')
def index():
return '這是首頁'
class ArticleView(Resource):
resource_fields = {
'article_title': fields.String(attribute='title'),
'article_content': fields.String(attribute='content'),
'article_author': fields.Nested({
'username': fields.String,
'email': fields.String
}, attribute='author'),
'article_tags': fields.List(fields.Nested({
'id': fields.Integer,
'name': fields.String
}), attribute='tags')
}
@marshal_with(resource_fields)
def get(self, article_id):
article = Article.query.get(article_id)
return article
api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')
if __name__ == '__main__':
app.run(debug=True)
再訪問http://127.0.0.1:5000/article/1,看到:
顯然, 顯示的屬性是重命名之後的屬性。
4.指定默認值
在返回一些字段的時候,有時候可能沒有值,那麼這時候可以在指定fields時給定一個默認值。
修改主程序文件實現指定默認值示例如下:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
import config
from exts import db
from models import Article
app = Flask(__name__)
api = Api(app)
app.config.from_object(config)
db.init_app(app)
@app.route('/')
def index():
return '這是首頁'
class ArticleView(Resource):
resource_fields = {
'article_title': fields.String(attribute='title'),
'article_content': fields.String(attribute='content'),
'article_author': fields.Nested({
'username': fields.String,
'email': fields.String
}, attribute='author'),
'article_tags': fields.List(fields.Nested({
'id': fields.Integer,
'name': fields.String
}), attribute='tags'),
'read_count': fields.Integer(default=0)
}
@marshal_with(resource_fields)
def get(self, article_id):
article = Article.query.get(article_id)
return article
api.add_resource(ArticleView, '/article/<article_id>', endpoint='article')
if __name__ == '__main__':
app.run(debug=True)
再訪問http://127.0.0.1:5000/article/1,看到:
顯然, 增加了文章的默認閱讀量爲0,返回的頁面中也將其渲染出來。
在給模型中存在的字段設置默認值時,如果數據庫中對應記錄的該字段的值存在,則頁面渲染數據庫中的值;
如果數據庫中該字段的值不存在即爲空時,則渲染給定的默認值。