From c56d07c5c07ba539402c97c4b8ba461472b08794 Mon Sep 17 00:00:00 2001 From: azhengzz <820108271@qq.com> Date: Wed, 31 Mar 2021 09:18:12 +0800 Subject: [PATCH] =?UTF-8?q?HTTP=E8=AF=B7=E6=B1=82=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=BC=A0=E5=85=A5=E8=AF=B7=E6=B1=82=E5=A4=B4?= =?UTF-8?q?=E6=95=B0=E6=8D=AE(=E5=8F=82=E8=80=83PostMan)=EF=BC=8C=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E8=B0=83=E8=AF=95=E6=89=A7=E8=A1=8C=E5=8D=95=E4=B8=AA?= =?UTF-8?q?HTTP=E8=AF=B7=E6=B1=82=20https://gitee.com/azhengzz/api-automat?= =?UTF-8?q?ion-test/issues/I3E5Z5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/cores/case/http/dispatcher.py | 11 ++- .../app/cores/case/http/request_util.py | 12 +++ ApiAutomationTest/app/models.py | 58 +++++++++++++- ApiAutomationTest/app/routes/ajax/routes.py | 7 ++ .../app/static/js/case/http/http.js | 80 ++++++++++++++++++- .../app/static/js/scene/scene.js | 3 +- .../app/templates/case/http/http.html | 10 +++ 7 files changed, 173 insertions(+), 8 deletions(-) diff --git a/ApiAutomationTest/app/cores/case/http/dispatcher.py b/ApiAutomationTest/app/cores/case/http/dispatcher.py index 6de5b46..26748ad 100644 --- a/ApiAutomationTest/app/cores/case/http/dispatcher.py +++ b/ApiAutomationTest/app/cores/case/http/dispatcher.py @@ -1,10 +1,12 @@ # coding=utf-8 +from copy import deepcopy + from app.models import Case, Dispatcher from app.cores.logger import DispatcherLogger from app.cores.dictionaries import DISPATCHER_TYPE, REPORT_RESULT from app.cores.dispatcher import AbstractCaseDispatcher -from app.cores.case.http.request_util import handle_url +from app.cores.case.http.request_util import handle_url, handle_headers from app.cores.case.http.http_cookie_pool_manager import HTTPCookiePoolManager from app.cores.case.http.http_request_header_pool_manager import HTTPRequestHeaderPoolManager from app.cores.case.base.script import exec_postprocessor_script, exec_preprocessor_script @@ -40,8 +42,6 @@ class HTTPCaseDispatcher(AbstractCaseDispatcher): # cookie数据 self.rcj = None - # http请求头 - self.headers = None # http请求字段 self.protocol = None @@ -57,6 +57,7 @@ class HTTPCaseDispatcher(AbstractCaseDispatcher): self.parameters = None self.expectations = None self.file_upload = None + self.headers = None def set_up(self): super().set_up() @@ -64,6 +65,8 @@ class HTTPCaseDispatcher(AbstractCaseDispatcher): self.rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=self.dispatcher_type) # 获取HTTP请求头 self.headers = HTTPRequestHeaderPoolManager.get_http_request_header(project_id=self.case.scene.module.project.id) + self.headers = deepcopy(self.headers) # 后面会对请求头数据更新,但不要影响HTTPRequestHeaderPool中的数据 + # 预处理脚本执行 preprocessor_script = self.case.specific_case.preprocessor_script_ exec_preprocessor_script( @@ -87,6 +90,8 @@ class HTTPCaseDispatcher(AbstractCaseDispatcher): self.parameters = self.case.specific_case.parameters self.expectations = self.case.specific_case.expectations self.file_upload = self.case.specific_case.file_upload + _headers = self.case.specific_case.headers + self.headers.update(handle_headers(_headers)) # 将HTTPCase中定义的headers覆盖到HTTPRequestHeaderManagerTool中 def execute(self): super().execute() diff --git a/ApiAutomationTest/app/cores/case/http/request_util.py b/ApiAutomationTest/app/cores/case/http/request_util.py index e0aa6fb..ad9d5a5 100644 --- a/ApiAutomationTest/app/cores/case/http/request_util.py +++ b/ApiAutomationTest/app/cores/case/http/request_util.py @@ -31,6 +31,18 @@ def handle_params(params): return ret +def handle_headers(headers): + """ + 处理请求头数据 + :param headers: 请求头数据 + :type headers: List[HTTPCaseHeader] + """ + ret = {} + for header in headers: + ret[header.name_] = header.value_ + return ret + + def handle_data(params, content_type=None): """ 处理请求参数,使其满足post请求的数据格式 diff --git a/ApiAutomationTest/app/models.py b/ApiAutomationTest/app/models.py index 28c0809..52d2247 100644 --- a/ApiAutomationTest/app/models.py +++ b/ApiAutomationTest/app/models.py @@ -1731,6 +1731,7 @@ class HTTPCase(db.Model): 关系说明 case: 案例组件 Case:HTTPCase is 1:1 parameters: 案例参数 HTTPCase:HTTPCaseParameter is 1:N + headers: 请求头参数 HTTPCase:HTTPCaseHeader is 1:N expectations: 案例期望 HTTPCase:HTTPCaseExpectation is 1:N file_upload: 文件上传参数 HTTPCase:HTTPCaseFileUpload is 1:N """ @@ -1753,6 +1754,7 @@ class HTTPCase(db.Model): case_id = db.Column(db.Integer, db.ForeignKey('case.id')) case = db.relationship('Case') parameters = db.relationship('HTTPCaseParameter', back_populates='http_case', cascade='all, delete-orphan') # 解除与case的关系后删除 + headers = db.relationship('HTTPCaseHeader', back_populates='http_case', cascade='all, delete-orphan') expectations = db.relationship('HTTPCaseExpectation', back_populates='http_case', cascade='all, delete-orphan') file_upload = db.relationship('HTTPCaseFileUpload', back_populates='http_case', cascade='all, delete-orphan') @@ -1788,7 +1790,7 @@ class HTTPCase(db.Model): @classmethod def add(cls, name, description, scene_id, protocol, domain, port, method, path, encoding, expectation_logic, message_body, content_type, preprocessor_script, postprocessor_script, status=STATUS.NORMAL, - parameters=None, file_upload=None, expectations=None): + parameters=None, headers=None, file_upload=None, expectations=None): """ 新增一条案例 :param name: 案例名 @@ -1807,6 +1809,7 @@ class HTTPCase(db.Model): :param preprocessor_script: 预处理脚本 :param postprocessor_script: 后处理脚本 :param parameters: 参数数据 + :param headers: 请求头数据 :param file_upload: 文件上传参数数据 :param expectations: 期望数据 """ @@ -1832,6 +1835,7 @@ class HTTPCase(db.Model): ) http_case.case = case http_case._update_parameters(parameters) + http_case._update_headers(headers) http_case._update_expectations(expectations) http_case._update_file_upload(file_upload) db.session.add(case) @@ -1841,8 +1845,8 @@ class HTTPCase(db.Model): @classmethod def update(cls, id, name, description, protocol, domain, port, method, path, encoding, expectation_logic, - message_body, content_type, preprocessor_script, postprocessor_script, params=None, file_upload=None, - expectations=None): + message_body, content_type, preprocessor_script, postprocessor_script, params=None, headers=None, + file_upload=None, expectations=None): """ 更新一条案例 :param id: 案例编号 @@ -1860,6 +1864,7 @@ class HTTPCase(db.Model): :param preprocessor_script: 预处理脚本 :param postprocessor_script: 后处理脚本 :param params: 参数数据 + :param headers: 请求头数据 :param file_upload: 文件上传参数 :param expectations: 期望数据 """ @@ -1881,6 +1886,7 @@ class HTTPCase(db.Model): http_case.preprocessor_script = preprocessor_script http_case.postprocessor_script = postprocessor_script http_case._update_parameters(params) + http_case._update_headers(headers) http_case._update_file_upload(file_upload) http_case._update_expectations(expectations) db.session.commit() @@ -1911,6 +1917,7 @@ class HTTPCase(db.Model): preprocessor_script=self.preprocessor_script, postprocessor_script=self.postprocessor_script, parameters=self.parameters, + headers=self.headers, expectations=self.expectations, file_upload=self.file_upload, ) @@ -1944,6 +1951,22 @@ class HTTPCase(db.Model): ) self.parameters.append(parameter) + def _update_headers(self, headers): + self.headers = [] + if headers is not None: + for _header in headers: + if hasattr(_header, 'get'): + header = HTTPCaseHeader( + name=_header.get('name'), + value=_header.get('value'), + ) + else: # copy_from_self func exec this branch + header = HTTPCaseHeader( + name=_header.name, + value=_header.value, + ) + self.headers.append(header) + def _update_expectations(self, expectations): self.expectations = [] if expectations is not None: @@ -2020,6 +2043,35 @@ class HTTPCaseParameter(db.Model): return _p(self.value) +class HTTPCaseHeader(db.Model): + """ + 字段说明 + name: 参数名 + value: 参数值 + 关系说明 + http_case: 请求头所属HTTP案例 HTTPCase:HTTPCaseHeader is 1:N + """ + # 需要转换为json数据格式的字段 + render_field_list = ['name', 'value'] + # field + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(512), nullable=False, default='', server_default='') + value = db.Column(db.String(512), nullable=False, default='', server_default='') + + # relationship + http_case_id = db.Column(db.Integer, db.ForeignKey('http_case.id')) + http_case = db.relationship('HTTPCase', back_populates='headers') + + # 解析处理 + @property + def name_(self): + return _p(self.name) + + @property + def value_(self): + return _p(self.value) + + class HTTPCaseExpectation(db.Model): """ 字段说明 diff --git a/ApiAutomationTest/app/routes/ajax/routes.py b/ApiAutomationTest/app/routes/ajax/routes.py index 8c2ce3d..4629924 100644 --- a/ApiAutomationTest/app/routes/ajax/routes.py +++ b/ApiAutomationTest/app/routes/ajax/routes.py @@ -54,6 +54,8 @@ def save_http_case(): if not exist: return form_content_type exist, form_param_json = get_form_from_request(request, 'param_json') if not exist: return form_param_json + exist, form_header_json = get_form_from_request(request, 'header_json') + if not exist: return form_header_json exist, form_file_upload_json = get_form_from_request(request, 'file_upload_json') if not exist: return form_file_upload_json exist, form_expectation_json = get_form_from_request(request, 'expectation_json') @@ -66,6 +68,7 @@ def save_http_case(): if not exist: return form_postprocessor_script try: params = json.loads(form_param_json) + headers = json.loads(form_header_json) file_upload = json.loads(form_file_upload_json) expectations = json.loads(form_expectation_json) if not Case.exist_and_status_not_delete(id=form_id): @@ -85,6 +88,7 @@ def save_http_case(): encoding=form_content_encoding, expectation_logic=form_expectation_logic, params=params, + headers=headers, file_upload=file_upload, expectations=expectations, message_body=form_message_body_json, @@ -2503,10 +2507,12 @@ def case(): data_expectations = json.loads(render_to_json(case.specific_case.expectations)) if case.case_type == CASE_TYPE.HTTP: data_parameters = json.loads(render_to_json(case.specific_case.parameters)) + data_headers = json.loads(render_to_json(case.specific_case.headers)) data_file_upload = json.loads(render_to_json(case.specific_case.file_upload)) table_cookie_manager_data = json.loads(render_to_json(case.specific_case.file_upload)) else: data_parameters = [] + data_headers = [] data_file_upload = [] table_cookie_manager_data = [] except Exception as e: @@ -2524,6 +2530,7 @@ def case(): 'html_text': html_text, 'data_expectations': data_expectations, 'data_parameters': data_parameters, + 'data_headers': data_headers, 'data_file_upload': data_file_upload, 'table_cookie_manager_data': table_cookie_manager_data, }) diff --git a/ApiAutomationTest/app/static/js/case/http/http.js b/ApiAutomationTest/app/static/js/case/http/http.js index 85ebc2c..95b1ab8 100644 --- a/ApiAutomationTest/app/static/js/case/http/http.js +++ b/ApiAutomationTest/app/static/js/case/http/http.js @@ -276,16 +276,19 @@ function getHTTPCaseElement(case_id, case_type) { element.dom.$input_content_encoding = $(`#input-content-encoding-${case_id}`); element.dom.$input_content_type = $(`#input-content-type-${case_id}`); element.dom.$div_table_parameter_spinner = $(`#div-table-parameter-spinner-${case_id}`); + element.dom.$div_table_header_spinner = $(`#div-table-header-spinner-${case_id}`); element.dom.$div_table_file_upload_spinner = $(`#div-table-file-upload-spinner-${case_id}`); element.dom.$nav_link_param_tab = $(`#param-tab-${case_id}`); + element.dom.$nav_link_header_tab = $(`#header-tab-${case_id}`); element.dom.$nav_link_file_upload_tab = $(`#file-upload-tab-${case_id}`); element.obj.table_parameter = null; // 请求参数表格 + element.obj.table_header = null; // 请求头表格 element.obj.table_file_upload = null; // 文件上传表格 element.obj.ace_message_body_editor = null; // 消息体数据格式化编辑器 element.obj.splitterMinSize = [50, 70]; // splitter分割条最小长度 - element.init = function (data_expectations, data_parameters, data_file_upload) { + element.init = function (data_expectations, data_parameters, data_headers, data_file_upload) { // 参数 // data_expectations: 案例期望数据 // data_parameters: 案例请求参数数据 @@ -301,6 +304,8 @@ function getHTTPCaseElement(case_id, case_type) { renderAceEditor(); // 渲染参数表格 renderParameterTable(data_parameters); + // 渲染请求头表格 + renderHeaderTable(data_headers); // 渲染文件上传表格 renderFileUploadTable(data_file_upload); // 获取CookieManager对象并初始化 @@ -336,6 +341,7 @@ function getHTTPCaseElement(case_id, case_type) { // 事件绑定 function eventBinding() { element.dom.$nav_link_param_tab.on('shown.bs.tab', clickParamTab); + element.dom.$nav_link_header_tab.on('shown.bs.tab', clickHeaderTab); element.dom.$nav_link_file_upload_tab.on('shown.bs.tab', clickFileTab); function clickParamTab() { @@ -343,6 +349,11 @@ function getHTTPCaseElement(case_id, case_type) { $(`#table-parameter-${case_id}`).handsontable('getInstance').render(); } + function clickHeaderTab() { + // 渲染ht + $(`#table-header-${case_id}`).handsontable('getInstance').render(); + } + function clickFileTab() { // 渲染ht $(`#table-file-upload-${case_id}`).handsontable('getInstance').render(); @@ -453,6 +464,72 @@ function getHTTPCaseElement(case_id, case_type) { } } + // 渲染请求头表格 + function renderHeaderTable(data_headers) { + const option_table_header = { + data: data_headers, + rowHeaders: true, + // colHeaders: true, + licenseKey: 'non-commercial-and-evaluation', + // 表格宽度 + // width: '95vw', // 不指定宽度使其自适应容器 + // 拉伸方式 + stretchH: 'all', + // tab键自动换行切换 + autoWrapRow: true, + height: '40vh', + // 最大行数 + maxRows: 99, + // 允许手工移动行或列 + manualRowResize: true, + manualColumnResize: true, + // 列名 + colHeaders: [ + '名称', + '值', + ], + // 为列设置默认值 + dataSchema: { + name: '', + value: '', + url_encode: false, + content_type: 'text/plain', + include_equals: true, + }, + // 设置列数据类型 + columns: [ + { + data: 'name' + }, + { + data: 'value', + }, + ], + // 列宽比例 + colWidths: [1, 1], + manualRowMove: true, + manualColumnMove: false, + // 右键菜单 + contextMenu: ['row_above', 'row_below', '---------', 'remove_row', '---------', 'undo', 'redo', '---------', 'alignment', '---------', 'copy', 'cut'], + // 列是否支持过滤 + filters: false, + // 下拉菜单 + dropdownMenu: ['make_read_only', '---------', 'alignment'], + // 语言 + language: 'zh-CN', + // 是否允许无效数据 默认 true + allowInvalid: false, + afterLoadData: afterLoadDataHookForTableParam, + }; + let $container = $(`#table-header-${case_id}`); + element.obj.table_header = $container.handsontable(option_table_header).handsontable('getInstance'); + + // 数据加载完毕后隐藏spinner + function afterLoadDataHookForTableParam() { + element.dom.$div_table_header_spinner.css('display', 'none'); + } + } + // 渲染文件上传表格 function renderFileUploadTable(data_file_upload) { // const container_table_file_upload = document.getElementById(`table-file-upload-${case_id}`); @@ -561,6 +638,7 @@ function getHTTPCaseElement(case_id, case_type) { expectation_logic: expectation_logic, content_type: content_type, param_json: JSON.stringify(element.obj.table_parameter.getSourceData()), + header_json: JSON.stringify(element.obj.table_header.getSourceData()), file_upload_json: JSON.stringify(element.obj.table_file_upload.getSourceData()), expectation_json: JSON.stringify(element.obj.table_expectation.getSourceData()), message_body_json: message_body_json_str, diff --git a/ApiAutomationTest/app/static/js/scene/scene.js b/ApiAutomationTest/app/static/js/scene/scene.js index 3a2fd9f..3a47c83 100644 --- a/ApiAutomationTest/app/static/js/scene/scene.js +++ b/ApiAutomationTest/app/static/js/scene/scene.js @@ -1330,6 +1330,7 @@ function showCase(case_id, case_type) { if(case_type === 'HTTP'){ // 渲染ht $(`#table-parameter-${case_id}`).handsontable('getInstance').render(); + $(`#table-header-${case_id}`).handsontable('getInstance').render(); $(`#table-file-upload-${case_id}`).handsontable('getInstance').render(); } if(!element_case_first_show_flag[case_id]){ // 只在第一次打开案例组件时渲染 @@ -1850,7 +1851,7 @@ function addCaseElement(case_id, case_type) { element_case_first_show_flag[case_id] = false; }else if(data.case_type === 'HTTP'){ let element = getHTTPCaseElement(case_id, data.case_type); - element.init(data.data_expectations, data.data_parameters, data.data_file_upload); + element.init(data.data_expectations, data.data_parameters, data.data_headers, data.data_file_upload); element_case_instance_dict[case_id] = element; element_case_first_show_flag[case_id] = false; } diff --git a/ApiAutomationTest/app/templates/case/http/http.html b/ApiAutomationTest/app/templates/case/http/http.html index 303c254..b1816f8 100644 --- a/ApiAutomationTest/app/templates/case/http/http.html +++ b/ApiAutomationTest/app/templates/case/http/http.html @@ -102,6 +102,9 @@ + {# #} @@ -123,6 +126,13 @@
{{ case.specific_case.message_body }}
+
+
+
+ 加载中Loading... +
+
+