开源一个通用的 HTTP 请求前端组件

news2024/9/24 13:26:04

像 Postman 这样可视化的 HTTP 请求工具是调试 API 不可或缺的利器。Postman 虽好但也越来越重,而且如果要整合到其他工具中,显然 Postman 又不是一个可行的方案。于是我想打造一个简单的前端组件(widget),它是一个标准 Vue 可复用的组件,能够轻易地被整合。整个组件最终界面如下。
在这里插入图片描述
该组件特性:

  • 基于 vue2/iview 方案,标准 npm 前端项目
  • 代码短小精悍,不过几个文件,不到一千行代码。
  • 依赖只有 iview 和 vue-codemirror,低耦合
  • 功能简单清晰易上手,代码易于理解与扩展

构建这么一个小组件可以说根本没什么难度,我也是一边仿着 Postman,一边“画界面”,两三天就完事了。当然后续还有很多的想法,很多的功能需要添加。不过前期肯定先出个初版,从最简单的开始。

名字就叫平淡无奇的 “api-helper” 吧~ 在线演示 | 源码 | NPM 发布页

使用方式

这是标准 vue 组件,安装组件:

npm i @ajaxjs/aj-api-helper

或者在 package.json 中添加依赖然后执行 npm i

  "dependencies": {
    "@ajaxjs/aj-api-helper": "1.0.0"
    ……
  },

引入方式:

import ApiHelperMain from "@ajaxjs/aj-api-helper";

export default {
  components: { ApiHelper: ApiHelperMain.ApiHelper },
  ……
}

标签中引入:<ApiHelper />

开发历程心得

整体界面

整体界面就是调用 ivew 组件库,这部分没什么好多说的了。代码编辑器使用了 vue-codemirror,也比较简单。

<!-- JSON 源码编辑器-->
<codemirror class="code-editor" v-model="responseBody" :options="cmOption" style="height:300px;"></codemirror>

vue-codemirror 配置如下:

cmOption: {
    tabSize: 4,
    styleActiveLine: true,
    lineNumbers: true,
    mode: "application/json",
    // theme: "monokai"
},

编辑表格

各种 Form、QueryString、Head 需要一个表格放置参数,类似于 Postman 的:
在这里插入图片描述
一开始打算使用 iView 的 table 组件,但感觉太笨重,于是还是用原生 <table> 自己搞一个。

在这里插入图片描述
这样无论源码还是界面显得清爽很多。实际源码如下:

<template>
  <table class="input-table">
    <thead>
      <th width="50"></th>
      <th>Key</th>
      <th>Value</th>
      <th>说明</th>
      <th>操作</th>
    </thead>
    <tr v-for="(item, index) in tableData" :key="index" :class="{disable: !item.enable}">
      <td align="center">
        <input type="checkbox" v-model="item.enable" />
      </td>
      <td>
        <input @focus="onInputFocus" @blur="onInoutBlur" @input="onInput(index)" v-model="item.key" />
      </td>
      <td>
        <input @focus="onInputFocus" @blur="onInoutBlur" v-model="item.value" />
      </td>
      <td>
        <input @focus="onInputFocus" @blur="onInoutBlur" v-model="item.desc" />
      </td>
      <td align="center">
        <Icon type="md-trash" class="delBtn" title="删除" @click="delRow(index)" />
      </td>
    </tr>
  </table>
</template>

<script>
export default {
  data() {
    return {
      tableData: this.data,
    };
  },
  props: {
    data: { type: Array, required: true },
  },
  methods: {
    onInputFocus(e) {
      let input = e.target;
      if (
        input.parentNode &&
        input.parentNode.parentNode &&
        input.parentNode.parentNode.tagName == "TR"
      ) {
        let tr = input.parentNode.parentNode;
        tr.classList.add("highlight");
      }
    },
    onInoutBlur(e) {
      let input = e.target;
      if (
        input.parentNode &&
        input.parentNode.parentNode &&
        input.parentNode.parentNode.tagName == "TR"
      ) {
        let tr = input.parentNode.parentNode;
        tr.classList.remove("highlight");
      }
    },
    onInput(index) {
      if (index + 1 == this.tableData.length) {
        // 最后一行
        this.tableData.push({
          enable: true,
          key: "",
          value: "",
          desc: "",
        });
      }
    },
    delRow(index) {
      if (this.tableData.length == 1) {
      } else {
        this.$delete(this.tableData, index);
      }
    },
  },
};
</script>

<style lang="less" scoped>
.input-table {
  width: 100%;
  border-collapse: collapse;

  input {
    border: 1px solid transparent;
    outline: none;
    padding: 0px 3px;
    width: 100%;
  }

  input:focus {
    border: 1px solid lightgray !important;
    background-color: white !important;
  }

  tr {
    &.highlight {
      background-color: #f9f9f9;

      input {
        border-color: #f9f9f9;
        background-color: #f9f9f9;
      }
    }

    &.disable {
      input {
        color: lightgray;
      }
    }
  }

  td,
  th {
    padding: 5px 5px;
    border: 1px solid lightgray;
  }
}

.delBtn {
  cursor: pointer;
}
</style>

输入的 tableData 格式如下。

 tableData: [
   {
     enable: true,
     key: "sdsd",
     value: "sdssds3",
   },
   {
     enable: false,
     key: "sdsd",
     value: "sdssds3",
   },
 ],

XHR 请求

HTTP 请求的核心自然是 XMLHttpRequest(); 的使用。必须要高度订制化,而不是复用某个 xhr 组件。写原生 xhr 实际也很简单,参见我的 xhr.js 源码。

//  XHR 发送组件
export default {
    data() {
        return {
            loading: false,
            response: {
                readyState: 0,
                status: 0,
                elapsed: 0,
            },
        };
    },

    methods: {
        doRequest(method, url, params, cfg) {
            let el = new Date();
            let xhr = new XMLHttpRequest();
            xhr.open(method, url);
            xhr.timeout = 5000; // 设置超时时间为5秒
            xhr.ontimeout = () => this.loading = false;// 请求超时后的处理
            xhr.onreadystatechange = () => {
                this.loading = true;
                this.response.readyState = xhr.readyState;
                this.response.status = xhr.status;

                if (xhr.readyState === 4) {
                    try {
                        if (!xhr.responseText) {
                            this.$Message.error('服务端返回空的字符串');
                            this.loading = false;

                            return;
                        }

                        // 跨域可能不能获取完整的响应头 https://qzy.im/blog/2020/09/can-not-get-response-header-using-javascript-in-cors-request/
                        let heads = xhr.getAllResponseHeaders();
                        heads = heads.split(';').join('\n');
                        this.responseHead = heads;

                        let parseContentType = cfg && cfg.parseContentType;
                        switch (parseContentType) {
                            case "text":
                                data = responseText;
                                break;
                            case "xml":
                                data = xhr.responseXML;
                                break;
                            case "json":
                            default:
                                this.responseBody = JSON.stringify(JSON.parse(xhr.responseText), null, 2);
                        }
                    } catch (e) {
                        alert("HTTP 请求错误:\n" + e + "\nURL: " + url); // 提示用户 异常
                    } finally {
                        this.loading = false;
                        this.response.elapsed = new Date() - el;
                    }
                }
            };

            let requestAll = 'HEAD \n' + method.toUpperCase() + ' ' + url + '\n';

            if (cfg && cfg.header) {
                for (let i in cfg.header) {
                    requestAll += i + " : " + cfg.header[i] + '\n';
                    xhr.setRequestHeader(i, cfg.header[i]);
                }
            }

            if (params)
                requestAll += 'BODY:\n' + params;

            this.requestAll = requestAll;
            xhr.send(params || null);
        },

        formatStatusCode() {
            let code = this.response.status;
            let str = code + '';

            if (str[0] === '2')
                return `<span style="color:green">${code}</span>`;
            else if (str[0] === '4' || str[0] === '5')
                return `<span style="color:red">${code}</span>`;
            else
                return str;
        }
    },
};

它基于 vue 的 mixins 特性进行分离。主意是控制一些请求状态之类的,已经返回特定的数据给前端显示。

工具函数

还有一些小的工具函数值得说下。

格式化 JSON

格式化 JSON,利用 JSON.stringify(),指定第三个参数即可加入缩进(indent)。

formatJs() {
    let json = this.requestParams.raw.json;
    json = JSON.stringify(JSON.parse(json), null, 4);
    this.requestParams.raw.json = json;
}

读写剪贴板

用 Javascript 的 navigator.clipboard 对象来复制文本或图片到剪贴板,但运行时会遇到以下错误:

navigator.clipboard undefined

这个错误的原因是 navigator.clipboard 对象只能在安全网络环境中才能使用,换言之,localhost、127.0.0.1 或者 https 中才能正常使用,否则用 http 或 IP 地址不允许访问。

最终的读取方法:

// 读取粘贴板
try {
    navigator.clipboard.readText().then((v) => {
        console.log("获取剪贴板成功:", v);
        this.requestParams.head.unshift({
            enable: true,
            key: "Authorization",
            value: "Bearer " + v,
            desc: "认证用的 token",
        });
    }).catch((v) => {
        console.log("获取剪贴板失败: ", v);
    });
} catch (e) {
    console.log(e);
    this.$Message.error('不支持读取粘贴板');
}

写入剪切板却有兼容方法。

/**
* 复制文字到剪切板
* 
* @param {*} text 
*/
aj.copyToClipboard = function (text) {
    if (navigator.clipboard) {
        // clipboard api 复制
        navigator.clipboard.writeText(text);
    } else {
        var textarea = document.createElement('textarea');
        document.body.appendChild(textarea);
        // 隐藏此输入框
        textarea.style.position = 'fixed';
        textarea.style.clip = 'rect(0 0 0 0)';
        textarea.style.top = '10px';
        // 赋值
        textarea.value = text;
        // 选中
        textarea.select();
        // 复制
        document.execCommand('copy', true);
        // 移除输入框
        document.body.removeChild(textarea);
    }
}

小结

虽然这只是个小工具,但仍有不少的想象空间。不一定都把功能加到这个组件里面,但可能跟其他组件有更多的联动。希望我有时间,能够进一步丰富各种功能,也希望你们用户能提出多的宝贵意见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/400277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

天池 DeepRec CTR 模型性能优化大赛 - 夺冠技术分享

作者&#xff1a;niceperf 团队 (李扬, 郭琳) 大家好&#xff0c;我们是 niceperf 团队&#xff0c;在天池 DeepRec CTR 模型性能优化大赛中&#xff0c;很荣幸取得了冠军的成绩 (Top 1/3802)。这篇文章复盘一下我们的参赛经验&#xff0c;希望对大家有所启发。 1.背景介绍 …

KDZD5035系列电缆试验油杯

一、概述 武汉凯迪正大总结十多年的局放试验经验&#xff0c;开发生产了KDZD5035系列电缆试验油杯终端&#xff0c;具有使用方便&#xff0c;性能可靠&#xff0c;本身局放量小等优点&#xff0c;与早期落地式油杯相比&#xff0c;可为用户节约大量的试验成本。 KDZD5520交流…

单片机学习笔记之点阵(8x8)

心血来潮&#xff0c;想捡一下丢了很久的单片机&#xff0c;纪录一下单片机学习简单的点阵显示&#xff0c;及踩到的䟘&#xff0c;找到吃灰很久的普中科技开发板&#xff08;非广告&#xff0c;为毕设学习买的&#xff09;。 1. 使用工具 使用开发板&#xff1a; 普中科技开发…

Hive---自定义函数

Hive自定义函数 文章目录Hive自定义函数定义自定义函数步骤创建一个Maven工程&#xff0c;导入依赖创建自定义函数类在 hive 的命令行窗口创建函数创建临时函数创建永久函数UDF打成 jar 包上传到服务器/opt/soft/hive312/lib/目录下将 jar 包添加到 hive 的 classpath建临时函数…

python数据类型与数据结构

目录 一、数据类型 1.1变量与常量 1.1.1变量 1.1.2常量 1.2字符串类型 1.3整数与浮点数 1.4List列表 1.5 元组tuple 1.6字典dict 二、字符串格式化 三、数据输入和类型转换 四、简单列表习题练习 一、数据类型 变量类型&#xff1a; 整数int&#xff08;4字节&#x…

IR-825 Biotin,IR 825 Biotin,IR825 Biotin,IR-825可以进行修饰生物素基团

IR825 Biotin&#xff0c;IR 825 Biotin&#xff0c;IR-825 Biotin | 生物素IR825荧光染料&#xff0c; 荧光染料IR825生物素&#xff0c;IR-825近红外染料 | CAS&#xff1a;N/A | 纯度&#xff1a;95%1.IR825 Biotin试剂信息&#xff1a;CAS&#xff1a;N/A外观&#xff1a;固…

字符串的使用

数组字符串转换 joinToString列表转成字符串 val str list.joinToString(",")split(“,”)字符串转成列表 val list1 str.split(",")subString()字符串截取 substring(0,2)这个只含开头不含结尾&#xff0c;索引从0开始 substring(2)这个表示截掉前两…

Genymotion模拟器安装

1.本节引言 如果你符合下述三种情况的话,你可以考虑安装一个Genymotion Android模拟器: 没有真机调试,只能用模拟器 嫌SDK内置的AVD启动速度,运行速度慢 电脑配置还可以,最好4G内存以上 如果你满足上述三种情况的话,那么装个比真机还快的Genymotion吧! 官方给出的介绍:…

Go爬虫学习笔记

N002.02 Go分布式爬虫实战 开篇 学习三阶段 入门&#xff0c;照猫画虎底层&#xff0c;了解方方面面&#xff0c;深入阅读源码和书籍借助开源组件来进行复杂设计&#xff0c;窥探各个组件赋能业务 分布式系统&#xff1a; 扩展性一致性可用性高并发微服务 爬虫&#xff1…

Java8使用Lambda表达式(流式)快速实现List转map 、分组、过滤等操作

利用java8新特性&#xff0c;可以用简洁高效的代码来实现一些数据处理。1 数据准备1.1 定义1个Fruit对象package com.wkf.workrecord.work;import org.junit.Test;import java.math.BigDecimal; import java.util.ArrayList; import java.util.List;/*** author wuKeFan* date …

Framework源码面试——Handler与事件传递机制面试集合

Handler面试题 Handler的作用&#xff1a; 当我们需要在子线程处理耗时的操作&#xff08;例如访问网络&#xff0c;数据库的操作&#xff09;&#xff0c;而当耗时的操作完成后&#xff0c;需要更新UI&#xff0c;这就需要使用Handler来处理&#xff0c;因为子线程不能做更新…

软件回归测试是什么?

一、软件回归测试是什么? 软件回归测试作为软件生命周期的一个组成部分&#xff0c;在整个软件测试过程中占有很大的工作量比重&#xff0c;软件开发的各个阶段都会进行多次回归测试。回归测试是指修改了旧代码后&#xff0c;重新进行测试以确认修改没有引入新的错误或导致其…

Boosting三巨头:XGBoost、LightGBM和CatBoost(发展、原理、区别和联系,附代码和案例)

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Binder ——binder的jni注册和binder驱动

环境&#xff1a;Android 11源码Android 11 内核源码源码阅读器 sublime textbinder的jni方法注册zygote启动1-1、启动zygote进程zygote是由init进程通过解析init.zygote.rc文件而创建的&#xff0c;zygote所对应的可执行程序是app_process&#xff0c;所对应的源文件是app_mai…

因果推断12--dragonnet论文和代码学习

目录 论文 dragonnet 1介绍 2 Dragonnet 3定向正则化 4相关工作 5实验 6讨论 NN-Based的模型 dragonnet 如何更新参数 dragonnet的损失函数 CausalML Dragonnet类 论文代码 论文 dragonnet Adapting Neural Networks for the Estimation of Treatment Effects 应…

二叉搜索树的实现

什么是二叉搜索树1.若它的左子树不为空&#xff0c;那么左子树上所有节点都小于根节点2.若它的右子树不为空&#xff0c;那么右子树上所有节点都小于根节点3.它的左右子树也分别是二叉搜索树4.使用中序遍历结果是从小到大定义节点&#xff0c;使用静态内部类static class TreeN…

http组成及状态及参数传递

http组成及状态及参数传递 早期的网页都是通过后端渲染来完成的&#xff1a;服务器端渲染&#xff08;SSR&#xff0c;server side render&#xff09;&#xff1a; 客户端发出请求 -> 服务端接收请求并返回相应HTML文档 -> 页面刷新&#xff0c;客户端加载新的HTML文档&…

7综合项目 旅游网 【7.精选分类】

精选旅游人气旅游→收藏次数最高最新旅游→日期最新主题旅游→主题关键字相同在首页将精选的内容动态展示的实现分析首页中的精选包含“人气旅游”、“最新旅游”、“主题旅游”三个部分index.html//页面加载完成,发送ajax请求根据点击不同分类展示不同内容人气旅游→收藏次数最…

分享17个提升开发效率的工具“轮子”

本文是向大家介绍平时在开发中经常用到的小工具&#xff0c;它能够极大得提升我们的开发效率&#xff0c;能够解决平时开发中遇到的问题。前言在java的庞大体系中&#xff0c;其实有很多不错的小工具&#xff0c;也就是我们平常说的“轮子“。如果在我们的日常工作当中&#xf…

数据结构课程设计:高铁信息管理系统(C++实现)

目录 简介实验输出实验要求代码运行环境结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金,有幸在竞赛中拿过一些国奖…