好久沒有使用python flask 框架了,今天有個想法,就是打造自己的一個在線http請求工具,網上有很多這樣的工具,寫這個東西本身沒有太多的技術含量,純粹是為了學習,順便鞏固一下flask。
話不多說先看效果
看上去很簡單,覺得就是個表單提交,實際上呢,也是需要一點處理邏輯的,麻雀雖小,五張俱全嘛。因為這個demo不需要數據庫,也不需要管理,所以,采用flask開發在合適不過了。廢話說完了,開始擼碼,真刀真槍開始干。
實現思路: 前端使用ajax提交表單到后臺,后臺通過requests 進行http請求并返回結果,然后將結果返回給前端進行展示,就是這么簡單。
第一步:環境準備:
1.python3.x
2.flask web框架
3.requests http請求框架
4.bootstrap 框架(界面)
5.pycharm 開發工具
第二步: 基本項目搭建
1.用pycharm新建一個flask項目,然后跑起來,這一步是為了驗證項目是否有問題。
2.在項目的templates目錄新建一個html文件,然后修改flask后臺代碼返回新建的html模版看能否正常顯示
from flask import render_template
@app.route('/')
def home():
return render_template('index.html')
3.修改html文件,引入bootstrap,完成基本的頁面布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>http在線工具</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet">
<!-- jQuery文件。務必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="col-sm-12" style="margin-top: 20px">
<div class="col-sm-6">
<form role="form">
<div class="form-group">
<label for="name">請求地址:</label>
<input type="text" class="form-control" id="url" placeholder="http://或https://">
</div>
<div class="form-group">
<label for="name">請求方式:</label>
<select class="form-control" id="method">
<option value="get" selected>get</option>
<option value="post">post</option>
</select>
</div>
<div class="form-group">
<label for="name">請求頭信息:</label>
<textarea class="form-control" id="headers" rows="3" placeholder="如:token:xxxx,a:xx多個參數用,隔開"></textarea>
</div>
<div class="form-group">
<label for="name">請求參數:</label>
<textarea class="form-control" rows="3" id="params" placeholder="如:a=1&b=2"></textarea>
</div>
<button id="btn" class="btn btn-primary" data-loading-text="Loading..."
type="button"> 提交
</button>
</form>
</div>
<div class="col-sm-6">
<ul class="nav nav-tabs" id="tabs">
<li class="active"><a href="#">格式化響應</a></li>
<li><a href="#">原始響應</a></li>
<li><a href="#">響應頭信息</a></li>
</ul>
<textarea id="result" style="margin-top: 10px;overflow: scroll;padding:10px;background-color:cornsilk;width: 100%;height: 350px">
</textarea>
</div>
</div>
</body>
</html>
4.重新啟動項目看看界面效果是否正常,沒有問題。
5.在app.py里新增一個函數用戶接收前端發來的參數
@app.route("/http", methods=['post'])
def http_request():
return "請求成功"
6.在前端頁面用jquery進行ajax請求:
$.ajax(({
url:'/http',
method:'post',
success:function(data){
alert(data)
},
error:function (e) {
alert("請求地址異常")
}
}))
如果可以正常的請求,基本的環境就搭建完成了。
第三步,邏輯的實現:
1.分析前端需要傳哪些參數到后臺,首先請求地址url、然后請求方式,method,還是請求頭信息headers和請求參數信息params。前端獲取輸入的這些值,基礎的就不多說了。
2.ajax請求添加這些參數
$.ajax({
url:'/http',
method:'post',
data:{url:url,method:method,headers:headers,params:params},
success:function(data){
response = data.body;
header = data.header;
show(response);
},
error:function (e) {
alert("請求地址異常")
}
})
- 修改后臺函數后臺進行參數接收:
@app.route("/http", methods=['post'])
def http_request():
url = request.form['url']
print('----url----',url)
method = request.form['method']
print('----method----',method)
headers = request.form['headers']
print('----headers----',headers)
params = request.form['params']
print('----params----',params)
return "請求成功"
在前端提交看看后臺能不能收到這些參數,如果不會flask參數接收自行到網上搜索一下。收到了參數就進行下一步。
4.處理接收到的參數,因為requests需要的參數不是字符串,所以需要轉換成一個字典。第一個就是處理請求頭信息,新增一個函數用戶處理頭信息:
def parse_headers(headers):
args = {}
if headers:
sps = headers.split(',')
for s in sps:
p = s.split(":")
print(p[0], "=", p[1])
args[p[0]] = p[1]
return args
這里需要前段端和后臺的參數約束,比如:多個參數用,號隔開,鍵和值之間用:,處理完成返回一個字典。
第二個就是處理請求參數的函數:
def parse_params(params):
args = {}
if params:
sps = params.split('&')
for s in sps:
p = s.split("=")
print(p[0], "=", p[1])
args[p[0]] = p[1]
return args
和請求頭信息處理類似,只是約束不一樣,我們多個參數使用&鏈接,鍵和值用=號。
5.使用requests進行http請求,一般都是使用
import requests
一般都是使用
requests.get()和requests.post;
但是為了統一采用以下方式進行請求,不清楚的可以看requests的相關文檔:
from requests import Session, Request
# 統一發起http請求
def send(url, method, headers, data):
s = Session()
req = Request(method, url,
data=data,
params=data,
headers=headers
)
prepped = req.prepare()
resp = s.send(prepped)
try:
res = {"header": "{0}".format(resp.headers), "body": "{0}".format(resp.content.decode(encoding='utf-8'))}
return Response(json.dumps(res), mimetype='application/json')
except:
res = {"header": "{0}".format(resp.headers), "body": "{0}".format(resp.content)}
return Response(json.dumps(res), mimetype='application/json')
這里面有個data和params參數,說明一下,為什么要傳兩個,data參數是針對post請求的params是get請求,因為這里的請求既支持get也支持post請求,這兩個參數都傳是為了保證post和get請求都能把參數傳輸過去。請求成功后,返回json給前端。我們要返回headr和body,因為前端需要展示請求頭信息和請求內容。這里需要注意的是得到的header信息和請求的content信息不能直接被序列化,所以需要轉換成字符串在以json的格式輸出,否則就會報錯,因為轉了字符串,導致編碼問題,所以需要用decode解碼,但是解碼的時候有些內容不支持utf-8,會產生異常,因此需要進行異常處理,保證把結果返回。
- 后臺調用:修改http_request函數
@app.route("/http", methods=['post'])
def http_request():
url = request.form['url']
method = request.form['method']
headers = parse_headers(request.form['headers'])
params = request.form['params']
args = parse_params(params)
return send(url, method, headers, args)
當然,還有需要完善的地方,比如參數校驗,我就偷懶不處理了??。
7.前端的展示
$.ajax({
url:'/http',
method:'post',
data:{url:url,method:method,headers:headers,params:params},
success:function(data){
response = data.body;
header = data.header;
show(response);
},
error:function (e) {
alert("請求地址異常")
}
})
//tab切換
$('#tabs li').click(function () {
$.each($('#tabs li'),function (i,item) {
item.className = '';
})
this.className = 'active';
switch ($(this).index()) {
case 0:
//展示格式化結果
show(response)
break;
case 1:
//展示原始結果
$('#result').val(response);
break;
case 2:
//展示響應頭信息
show(header)
break;
}
})
//展示請求結果
function show(data) {
try{
var json = formatJson(data)
$('#result').val(json);
}catch (e) {
$('#result').val(data);
}
}
//格式化json
function formatJson(json, options) {
var reg = null,
formatted = '',
pad = 0,
PADDING = ' '; // one can also use '\t' or a different number of spaces
// optional settings
options = options || {};
// remove newline where '{' or '[' follows ':'
options.newlineAfterColonIfBeforeBraceOrBracket = (options.newlineAfterColonIfBeforeBraceOrBracket === true) ? true : false;
// use a space after a colon
options.spaceAfterColon = (options.spaceAfterColon === false) ? false : true;
// begin formatting...
// make sure we start with the JSON as a string
if (typeof json !== 'string') {
json = JSON.stringify(json);
}
// parse and stringify in order to remove extra whitespace
json = JSON.parse(json);
json = JSON.stringify(json);
// add newline before and after curly braces
reg = /([\{\}])/g;
json = json.replace(reg, '\r\n$1\r\n');
// add newline before and after square brackets
reg = /([\[\]])/g;
json = json.replace(reg, '\r\n$1\r\n');
// add newline after comma
reg = /(\,)/g;
json = json.replace(reg, '$1\r\n');
// remove multiple newlines
reg = /(\r\n\r\n)/g;
json = json.replace(reg, '\r\n');
// remove newlines before commas
reg = /\r\n\,/g;
json = json.replace(reg, ',');
// optional formatting...
if (!options.newlineAfterColonIfBeforeBraceOrBracket) {
reg = /\:\r\n\{/g;
json = json.replace(reg, ':{');
reg = /\:\r\n\[/g;
json = json.replace(reg, ':[');
}
if (options.spaceAfterColon) {
reg = /\:/g;
json = json.replace(reg, ': ');
}
$.each(json.split('\r\n'), function(index, node) {
var i = 0,
indent = 0,
padding = '';
if (node.match(/\{$/) || node.match(/\[$/)) {
indent = 1;
} else if (node.match(/\}/) || node.match(/\]/)) {
if (pad !== 0) {
pad -= 1;
}
} else {
indent = 0;
}
for (i = 0; i < pad; i++) {
padding += PADDING;
}
formatted += padding + node + '\r\n';
pad += indent;
});
return formatted;
};
這里需要注意,結果的展示不要使用div,就使用textarea,不然josn格式化不會有效果。
第四步:測試
萬事具備了,就來測試一把,如果有什么錯誤就調試一下代碼了。
下面貼一下完整代碼:前端,html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>http在線工具</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet">
<!-- jQuery文件。務必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="col-sm-12" style="margin-top: 20px">
<div class="col-sm-6">
<form role="form">
<div class="form-group">
<label for="name">請求地址:</label>
<input type="text" class="form-control" id="url" placeholder="http://或https://">
</div>
<div class="form-group">
<label for="name">請求方式:</label>
<select class="form-control" id="method">
<option value="get" selected>get</option>
<option value="post">post</option>
</select>
</div>
<div class="form-group">
<label for="name">請求頭信息:</label>
<textarea class="form-control" id="headers" rows="3" placeholder="如:token:xxxx,a:xx多個參數用,隔開"></textarea>
</div>
<div class="form-group">
<label for="name">請求參數:</label>
<textarea class="form-control" rows="3" id="params" placeholder="如:a=1&b=2"></textarea>
</div>
<button id="btn" class="btn btn-primary" data-loading-text="Loading..."
type="button"> 提交
</button>
</form>
</div>
<div class="col-sm-6">
<ul class="nav nav-tabs" id="tabs">
<li class="active"><a href="#">格式化響應</a></li>
<li><a href="#">原始響應</a></li>
<li><a href="#">響應頭信息</a></li>
</ul>
<textarea id="result" style="margin-top: 10px;overflow: scroll;padding:10px;background-color:cornsilk;width: 100%;height: 350px">
</textarea>
</div>
</div>
<script>
var method = 'get';
var response='';
var header = '';
$('#method').change(function () {
method=$("#method").val();
})
$('#btn').click(function () {
var url = $('#url').val();
var headers = $('#headers').val();
var params = $('#params').val();
if(url==null||url.length==0){
alert("請求地址不能為空")
return;
}
if(!url.startsWith("http://")&&!url.startsWith("https://")){
alert("請求地址有誤")
return;
}
$(this).button('loading');
$.ajax({
url:'/http',
method:'post',
data:{url:url,method:method,headers:headers,params:params},
success:function(data){
$('#btn').button('reset');
response = data.body;
header = data.header;
show(response);
},
error:function (e) {
$('#btn').button('reset');
alert("請求地址異常")
}
})
})
//tab切換
$('#tabs li').click(function () {
$.each($('#tabs li'),function (i,item) {
item.className = '';
})
this.className = 'active';
switch ($(this).index()) {
case 0:
//展示格式化結果
show(response)
break;
case 1:
//展示原始結果
$('#result').val(response);
break;
case 2:
//展示響應頭信息
show(header)
break;
}
})
//展示請求結果
function show(data) {
try{
var json = formatJson(data)
$('#result').val(json);
}catch (e) {
$('#result').val(data);
}
}
//格式化json
function formatJson(json, options) {
var reg = null,
formatted = '',
pad = 0,
PADDING = ' '; // one can also use '\t' or a different number of spaces
// optional settings
options = options || {};
// remove newline where '{' or '[' follows ':'
options.newlineAfterColonIfBeforeBraceOrBracket = (options.newlineAfterColonIfBeforeBraceOrBracket === true) ? true : false;
// use a space after a colon
options.spaceAfterColon = (options.spaceAfterColon === false) ? false : true;
// begin formatting...
// make sure we start with the JSON as a string
if (typeof json !== 'string') {
json = JSON.stringify(json);
}
// parse and stringify in order to remove extra whitespace
json = JSON.parse(json);
json = JSON.stringify(json);
// add newline before and after curly braces
reg = /([\{\}])/g;
json = json.replace(reg, '\r\n$1\r\n');
// add newline before and after square brackets
reg = /([\[\]])/g;
json = json.replace(reg, '\r\n$1\r\n');
// add newline after comma
reg = /(\,)/g;
json = json.replace(reg, '$1\r\n');
// remove multiple newlines
reg = /(\r\n\r\n)/g;
json = json.replace(reg, '\r\n');
// remove newlines before commas
reg = /\r\n\,/g;
json = json.replace(reg, ',');
// optional formatting...
if (!options.newlineAfterColonIfBeforeBraceOrBracket) {
reg = /\:\r\n\{/g;
json = json.replace(reg, ':{');
reg = /\:\r\n\[/g;
json = json.replace(reg, ':[');
}
if (options.spaceAfterColon) {
reg = /\:/g;
json = json.replace(reg, ': ');
}
$.each(json.split('\r\n'), function(index, node) {
var i = 0,
indent = 0,
padding = '';
if (node.match(/\{$/) || node.match(/\[$/)) {
indent = 1;
} else if (node.match(/\}/) || node.match(/\]/)) {
if (pad !== 0) {
pad -= 1;
}
} else {
indent = 0;
}
for (i = 0; i < pad; i++) {
padding += PADDING;
}
formatted += padding + node + '\r\n';
pad += indent;
});
return formatted;
};
</script>
</body>
</html>
后臺:app.py
from flask import Flask, request, Response
from flask import render_template
from requests import Session, Request
import json
app = Flask(__name__)
# 頁面展示
@app.route('/')
def home():
return render_template('index.html')
# 處理http請求
@app.route("/http", methods=['post'])
def http_request():
url = request.form['url']
method = request.form['method']
headers = parse_headers(request.form['headers'])
params = request.form['params']
args = parse_params(params)
return send(url, method, headers, args)
# 統一發起http請求
def send(url, method, headers, data):
s = Session()
req = Request(method, url,
data=data,
params=data,
headers=headers
)
prepped = req.prepare()
resp = s.send(prepped)
try:
res = {"header": "{0}".format(resp.headers), "body": "{0}".format(resp.content.decode(encoding='utf-8'))}
return Response(json.dumps(res), mimetype='application/json')
except:
res = {"header": "{0}".format(resp.headers), "body": "{0}".format(resp.content)}
return Response(json.dumps(res), mimetype='application/json')
# 處理請求參數轉換成字典
def parse_params(params):
args = {}
if params:
sps = params.split('&')
for s in sps:
p = s.split("=")
print(p[0], "=", p[1])
args[p[0]] = p[1]
return args
# 處理請求頭信息轉換成字典
def parse_headers(headers):
args = {}
if headers:
sps = headers.split(',')
for s in sps:
p = s.split(":")
print(p[0], "=", p[1])
args[p[0]] = p[1]
return args
# 用于測試參數的接收和頭信息的接收
@app.route('/test', methods=['get', 'post'])
def test():
if request.method == 'GET':
a = request.args.get('a', '')
b = request.args.get('b', '')
token = request.headers.get('token')
return '測試get請求,參數 a={0},b={1},頭:token={2}'.format(a, b, token)
a = request.form['a']
b = request.form['b']
token = request.headers.get('token')
return '測試post請求,參數 a={0},b={1},頭:token={2}'.format(a, b, token)
if __name__ == '__main__':
app.run()
第五步:總結
到這里,使用flask擼一個http在線請求工具就完成了,當然這只是實現一個模擬過程,還需要做一些細節優化,廢話多了,只是想說的詳細點。因為代碼不多我就不上傳到github了,如果有需要的可以聯系我,如果有問題歡迎留言討論,喜歡我的文章記得關注我??哦!