不需要web服务器,如何构建一个可以内部跨域的http服务(Vue+Flask)
Posted 山河已无恙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不需要web服务器,如何构建一个可以内部跨域的http服务(Vue+Flask)相关的知识,希望对你有一定的参考价值。
写在前面
我的需求:
- 一个很老的项目,中游服务,
webservice
接口,需要测试,没有页面,需要我写一个小工具来测试,我准备用一个web来实现。
我需要解决的问题:
- 这个小工具其实类似测试工具,
soup UI
或者postman
,需要实现以下功能:- 满足跨域请求,尽可能的轻量。
- 满足发送
xml
和json
作为报文请求 - 可以做
简单的自动化压力测试
- 可以
存储所有的的接口报文信息
作为发送请求 - 可以
修改设置请求
url,选择存在的
url路径
- 可以展示少量的
请求报文和响应报文历史数据
- 做好的工具
不需要环境
可以在机器上直接运行
,类似windows
上的*.exe
我是是这样解决的:
- 在技术上,涉及到的技术栈:
Vue
+Flask
,主要是轻量 - 数据没有持久化,因为也没有多少数据,只是简单的使用
- 前后端分离的方式开发,打包方式:前端编译好直接放到后端的指定文件夹下,通过
python
的PyInstaller
打包为exe
- 直接运行
exe
就会在window发布为一个服务。不需要部署
。
需要注意的问题
- 前后端的整合
- 使用
PyInstaller
的打包问题 - 需要知道一点
Vue
和python
.
人生两苦,想要却不得,拥有却失去。 ----- 烽火戏诸侯《雪中悍刀行》
开发环境准备
这里不多讲,这是我的版本:
前端
PS > npm -v
6.12.1
PS > node -v
v12.13.1
PS > vue -V
3.7.0
后端
PS > python -V
Python 3.9.0
PS > pip -V
pip 20.2.3 from d:\\python\\python310\\lib\\site-packages\\pip (python 3.9)
PS > PyInstaller -v
4.7
前端把需要测试的接口地址,报文通过axios 发送给后端Flask服务,Flask服务通过 requests 模块实现测试
测试工具功能:
– |
---|
xml,json 格式的报文发送,支持http,soap协议 的方式 |
---|
支持请求报文路径自定义及相关配置 |
---|
支持测试接口历史的查看(少量) |
---|
支持简单压力测试,自定义时间间隔,轮询调用接口方式 |
---|
获取报文 |
---|
二、编码
后端编码
后端很简单,需要注意的是,设置静态资源的加载路径,以及设置跨域
from flask import Flask, jsonify,request,render_template
from flask_cors import CORS #跨域问题
import requests
import time
# configuration
DEBUG = True
# instantiate the app
app = Flask(__name__,static_folder = "./dist/static", template_folder = "./dist")
app.config.from_object(__name__)
# enable CORS
CORS(app, resources=r'/*': 'origins': '*')
headersXml =
"Content-Type": "Content-Type: text/xml;charset=UTF-8",
"Connection": "keep-alive",
headersJson =
"Content-Type": "application/json;charset=UTF-8",
"Connection": "keep-alive",
SendData = []
# sanity check route
@app.route('/test', methods=['POST','GET'])
def uag_test():
response = ""
if request.method == 'POST':
post_data = request.get_json()
data = post_data.get("content")
url = post_data.get("url")
id = post_data.get("id")
nameCose = post_data.get("nameCose")
type = post_data.get("type")
date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print("============================================",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),"================================================")
print("请求的URL:",url)
print("请求的报文:",data)
print("请求的ID:",id)
print("请求的日期:",date)
print("请求的报文类型:",type)
try:
if type == 1 :
responseDate=requests.post(url, headers=headersXml, data=data)
response = responseDate.text
if type == 2 :
responseDate=requests.post(url, headers=headersJson, data=data)
response = responseDate.text
except:
return jsonify("服务器异常!")
print("============================================",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),"================================================")
#response = etree.fromstring(response.encode('utf-8'))
#htmlelement = etree.XML(etree.tostring(response, pretty_print = True,encoding='utf-8'))
#print(etree.tostring(htmlelement))
SendData.insert(0,
"id":id,
"url":url,
"data":data,
"response":response ,
"date":date,
"nameCose":nameCose
)
if len(SendData) > 5 :
SendData.pop()
return jsonify(response)
# sanity check route
@app.route("/init",methods=['GET','POST'])
def uag_init():
print("获取全部数据")
return jsonify(SendData)
@app.route('/', defaults='path': '')
@app.route('/<path:path>')
def catch_all(path):
return render_template("index.html")
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8085,debug=DEBUG)
前端代码
vie.config.js 代码
let proxyObj = ;
proxyObj['/'] =
target: 'http://localhost:8086',
changeOrigin: true,
pathRewrite:
'^/': ''
module.exports =
devServer:
host: '127.0.0.1',
proxy: proxyObj,
,
lintOnSave:false,//关闭eslintre语法检查
assetsDir: 'static/',
main.js
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.config.productionTip = false
Vue.use(ElementUI);
Vue.use(VueAxios, axios)
new Vue(
render: h => h(App),
).$mount('#app')
router.js
import Vue from 'vue';
import Router from 'vue-router';
import Ping from './components/Ping.vue';
import App from './App.vue';
Vue.use(Router);
export default new Router(
mode: 'history',
base: process.env.BASE_URL,
routes: [
path: '/ping',
name: 'Ping',
component: Ping,
,
path: '/',
name: 'App',
component: App,
],
);
vue的相关页面代码在最后
三、前后端整合
前后端目录对应 |
---|
PyInstaller打包,运行测试
这里打包是通过PyInstaller
来完成的,如果为windows
系统打包,则为一个单独的app.exe
文件,windows上运行直接双击。linux
的话,是一个可以在机器上直接运行的二进制文件,linux上运行通过./app
来运行。
当然,PyInstaller
可以直接通过命令行的方式来运行,也可以通过py文件的方式,下面是一个打包的脚本。
from PyInstaller.__main__ import run
#### 打包文件直接执行
if __name__ == '__main__':
opts = ['app.py', # 主程序文件
'-F', # 打包单文件
'--icon=favicon.ico', # 可执行程序图标
'--add-data=dist;dist', # 打包包含的html页面
'--add-data=dist\\\\static;dist\\\\static', # 打包包含的静态资
]
run(opts)
直接运行就可以打包了。
python package.py
这里要说明一下文件对应的目录位置
对应的打包文件 |
---|
直接发布一个服务服务 |
整个文件目录 |
app.vue 代码
<template>
<div id="app">
<el-container>
<el-header><h1>接口测试小工具</h1></el-header>
<el-main class="main-class">
<div class="main-up-class">
<div class="search-class">
<!-- 搜索框 -->
<div class="search-class-up">
<div>
<el-select
v-model="ipValue"
placeholder="IP"
style="width: 300px"
>
<el-option
v-for="item in optionsIp"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</div>
<div>
<el-select
v-model="hostValue"
placeholder="端口"
style="width: 200px"
>
<el-option
v-for="item in optionsHost "
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</div>
<div>
<el-select
v-model="pathValue"
placeholder="请求路径"
style="width: 480px"
>
<el-option
v-for="item in optionsPath"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</div>
</div>
<div class="search-class-next">
<el-input placeholder="输入完整路径" clearable v-model="urlValue">
<el-button slot="append" @click = "testNet">测试网络</el-button>
</el-input>
</div>
</div>
<div class="active-class">
<el-collapse v-model="activeNames" @change="handleChange">
<el-collapse-item title="请求响应报文" name="1">
<div class="context-class">
<el-card
v-loading="loading"
shadow="never"
class="context-card-class"
>
<div slot="header" class="clearfix">
<span>请求报文</span>
<el-button
style="float: right; padding: 3px 0px 2px"
type="text"
@click="getPost"
>
请求
</el-button>
<el-button
style="float: right; padding: 3px "
type="text"
@click="cleartextareaRequest"
>
清空
</el-button>
<el-button
style="float: right; padding: 3px 0"
type="text"
@click="RequestTxt"
>
获取报文
</el-button>
<el-radio-group v-model="radio" style="float: right; padding: 3px 0">
<el-radio :label="1">text/xml</el-radio>
<el-radio :label="2">application/json</el-radio>
</el-radio-group>
</div>
<el-input
type="textarea"
:autosize=" minRows: 17, maxRows: 17 "
placeholder="请输入请求"
v-model="textareaRequest"
show-word-limit
autofocus="true"
>
</el-input>
</el-card>
<el-card shadow="never" class="context-card-class">
<div slot="header" class="clearfix">
<span>响应报文</span>
<el-button
style="float: right; padding: 3px 0"
type="text"
@click="clearTextareaResponse以上是关于不需要web服务器,如何构建一个可以内部跨域的http服务(Vue+Flask)的主要内容,如果未能解决你的问题,请参考以下文章