欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > 简单接口工具(ApiCraft-Web)

简单接口工具(ApiCraft-Web)

2025/4/19 16:19:44 来源:https://blog.csdn.net/bingbingyihao/article/details/147315194  浏览:    关键词:简单接口工具(ApiCraft-Web)

ApiCraft-Web

项目介绍

ApiCraft-Web 是一个轻量级的 API 测试工具,提供了简洁直观的界面,帮助开发者快速测试和调试 HTTP 接口。

功能特点

  • 支持多种 HTTP 请求方法(GET、POST、PUT、DELETE)
  • 可配置请求参数(Query Parameters)
  • 可配置请求头(Headers)
  • JSON 格式的请求体编辑器,支持语法高亮和格式化
  • 美观的响应数据展示
  • 显示响应状态码、响应时间和数据大小
  • 跨域支持

技术栈

前端

  • Vue 3
  • TailwindCSS
  • CodeMirror 6
  • Axios

后端

  • Spring Boot 2.3.4
  • RestTemplate
  • Lombok

快速开始

环境要求

  • Node.js 14+
  • JDK 8+
  • Maven 3+

安装和运行

  1. 克隆项目
git clone https://gitee.com/anxwefndu/ApiCraft-Web.git
cd ApiCraft-Web
  1. 启动后端服务
cd SpringBoot
mvn spring-boot:run
  1. 启动前端服务
cd code
npm install
npm run serve
  1. 访问应用
    打开浏览器访问:http://localhost:8081

使用说明

  1. 发送请求

    • 选择请求方法(GET、POST、PUT、DELETE)
    • 输入目标 URL
    • 根据需要添加查询参数、请求头和请求体
    • 点击"发送请求"按钮
  2. 查看响应

    • 响应状态码
    • 响应时间
    • 数据大小
    • 格式化的响应数据

开发计划

  • 支持更多请求方法
  • 请求历史记录
  • 接口集合管理
  • 环境变量配置
  • 响应数据导出
  • 暗色主题支持

源码下载

ApiCraft-Web

演示截图

1.系统首页
在这里插入图片描述

2.接口请求
在这里插入图片描述

核心源码

code/src/App.vue

<script setup>
import {ref, reactive} from 'vue';
import axios from 'axios';
import Message from '@/utils/message';
import JsonEditor from '@/components/JsonEditor.vue';// 请求方法
const method = ref('GET');// 请求URL
const url = ref('');// 当前选中的参数类型标签
const activeTab = ref('params'); // params, headers, body// 请求参数
const requestData = reactive({params: [{ key: '', value: '' }],headers: [{ key: 'Content-Type', value: 'application/json' },],body: ''
});// 响应数据
const response = reactive({status: '',time: '',size: '',data: null,loading: false
});// 添加参数
const addParam = () => {requestData.params.push({ key: '', value: '' });
};// 删除参数
const removeParam = (index) => {requestData.params.splice(index, 1);
};// 添加请求头
const addHeader = () => {requestData.headers.push({ key: '', value: '' });
};// 删除请求头
const removeHeader = (index) => {requestData.headers.splice(index, 1);
};// 切换参数类型标签
const switchTab = (tab) => {activeTab.value = tab;
};const jsonEditor = ref();
const hasJsonError = ref(false);const formatJsonBody = () => {jsonEditor.value?.formatJson();
};const handleJsonError = (error) => {hasJsonError.value = !!error;
};// 发送请求
const sendRequest = async () => {if (!url.value) {Message.warning('请输入请求URL');return;}if (hasJsonError.value) {Message.error('请求体 JSON 格式错误');return;}response.loading = true;try {// 构建请求参数const queryParams = {};const headers = {};requestData.params.forEach(param => {if (param.key && param.value) {queryParams[param.key] = param.value;}});requestData.headers.forEach(header => {if (header.key && header.value) {headers[header.key] = header.value;}});const requestBody = activeTab.value === 'body' ? requestData.body : null;// 发送请求到后端代理const result = await axios.post('http://localhost:8080/api/proxy', {url: url.value,method: method.value,headers: headers,queryParams: queryParams,body: requestBody});// 更新响应数据response.status = `${result.data.status} ${result.data.status === 200 ? 'OK' : ''}`;response.time = `${result.data.responseTime}ms`;response.size = result.data.contentLength;response.data = result.data.data;Message.success('请求成功');} catch (error) {Message.error(error.message || '请求失败');response.status = '500 Error';response.data = error.message;} finally {response.loading = false;}
};// 添加格式化响应数据的函数
const formatResponseData = (data) => {if (typeof data === 'string') {try {// 尝试解析字符串为 JSONreturn JSON.stringify(JSON.parse(data), null, 2);} catch {// 如果不是 JSON 字符串,直接返回原始字符串,去掉多余的引号return data.replace(/^"|"$/g, '');}}// 如果是对象,格式化为 JSONreturn JSON.stringify(data, null, 2);
};
</script><template><div class="bg-gray-50" style="width: 100%; height: 100%"><div class="w-[1200px] mx-auto"><nav class="h-16 bg-white shadow flex items-center justify-between px-8"><div class="flex items-center space-x-2"><span class="text-2xl font-['Pacifico'] text-primary">logo</span><span class="text-lg font-medium">API工具</span></div><button class="w-10 h-10 rounded-button flex items-center justify-center hover:bg-gray-100 transition-colors"><i class="fas fa-sun text-gray-600"></i></button></nav><main class="py-12"><div class="mx-auto"><div class="bg-white rounded-lg shadow p-8"><h2 class="text-lg font-semibold mb-4">接口测试</h2><div class="space-y-4"><div class="grid grid-cols-1 md:grid-cols-4 gap-4"><div><label class="block text-sm font-medium text-gray-700 mb-1">请求方法</label><div class="relative"><select v-model="method"class="block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-primary focus:border-primary rounded-button"><option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option></select></div></div><div class="md:col-span-3"><label class="block text-sm font-medium text-gray-700 mb-1">请求URL</label><div class="flex"><input type="text" v-model="url"class="flex-1 min-w-0 block w-full px-3 py-2 rounded-l-button border border-gray-300 focus:outline-none focus:ring-primary focus:border-primary"><buttonclass="bg-primary hover:bg-blue-600 text-white px-4 py-2 rounded-r-button text-sm font-medium" @click="sendRequest">发送</button></div></div></div><div><label class="block text-sm font-medium text-gray-700 mb-1">请求参数</label><div class="overflow-hidden border border-gray-300 rounded-button"><div class="bg-gray-50 px-4 py-2 border-b border-gray-300"><div class="flex items-center space-x-4"><button class="text-sm font-medium text-gray-500" :class="activeTab === 'params' ? ' text-primary ' : ''" @click="switchTab('params')">Query Params</button><button class="text-sm font-medium text-gray-500" :class="activeTab === 'headers' ? ' text-primary ' : ''" @click="switchTab('headers')">Headers</button><button class="text-sm font-medium text-gray-500" :class="activeTab === 'body' ? ' text-primary ' : ''" @click="switchTab('body')">Body</button></div></div><div class="bg-white"><div class="space-y-3 p-4" v-show="activeTab === 'params'"><template v-for="(param, index) in requestData.params" :key="index"><div class="grid grid-cols-12 gap-4 items-center"><div class="col-span-3"><input type="text" v-model="param.key" placeholder="参数名"class="block w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-primary focus:border-primary"></div><div class="col-span-8"><input type="text" v-model="param.value" placeholder="参数值"class="block w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-primary focus:border-primary"></div><div class="col-span-1"><button class="text-gray-500 hover:text-gray-700" @click="removeParam(index)"><i class="fas fa-trash fa-icon"></i></button></div></div></template><button class="text-sm text-primary hover:text-blue-600 flex items-center space-x-1"><i class="fas fa-plus fa-icon"></i><span @click="addParam">添加参数</span></button></div><div class="space-y-3 p-4" v-show="activeTab === 'headers'"><template v-for="(header, index) in requestData.headers" :key="index"><div class="grid grid-cols-12 gap-4 items-center"><div class="col-span-3"><input type="text" v-model="header.key" placeholder="参数名"class="block w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-primary focus:border-primary"></div><div class="col-span-8"><input type="text" v-model="header.value" placeholder="参数值"class="block w-full px-3 py-2 border border-gray-300 rounded-button focus:outline-none focus:ring-primary focus:border-primary"></div><div class="col-span-1"><button class="text-gray-500 hover:text-gray-700" @click="removeHeader(index)"><i class="fas fa-trash fa-icon"></i></button></div></div></template><button class="text-sm text-primary hover:text-blue-600 flex items-center space-x-1"><i class="fas fa-plus fa-icon"></i><span @click="addHeader">添加请求头</span></button></div><div class="space-y-3" v-if="activeTab === 'body'"><JsonEditorv-model="requestData.body"height="500px"ref="jsonEditor"@error="handleJsonError"/><div class="flex justify-end space-x-2" style="margin-bottom: 0.75rem; margin-right: 0.75rem"><button@click="formatJsonBody"class="text-sm text-primary hover:text-blue-600 flex items-center space-x-1"><i class="fas fa-code fa-icon"></i><span>格式化</span></button></div></div></div></div></div><div><label class="block text-sm font-medium text-gray-700 mb-1">响应结果</label><div v-if="response.data" class="border border-gray-300 rounded-button overflow-hidden"><div class="bg-gray-50 px-4 py-2 border-b border-gray-300 flex items-center justify-between"><div class="flex items-center space-x-2"><span class="text-sm font-medium">状态: {{ response.status }}</span><span class="text-sm text-gray-500">时间: {{ response.time }}</span><span class="text-sm text-gray-500">大小: {{ response.size }}</span></div><button @click="sendRequest" class="text-sm text-primary hover:text-blue-600"><i class="fas fa-redo fa-icon mr-1"></i><span>重新请求</span></button></div><div class="bg-white p-4"><pre class="whitespace-pre-wrap">{{ formatResponseData(response.data) }}</pre></div></div></div></div></div><div class="mt-12 bg-white rounded-lg shadow p-8"><h2 class="text-xl font-medium mb-6">使用说明</h2><div class="space-y-4 text-gray-600"><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>在请求 URL 输入框中输入完整的 API 地址,选择对应的请求方法(GET、POST、PUT、DELETE)</p></div><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>在请求参数区域可以设置 Query 参数、Headers 以及请求体(Body),支持多个参数的添加和删除</p></div><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>发送请求后,可以在响应结果区域查看状态码、响应时间、数据大小等信息,支持重新发送请求</p></div><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>响应数据会以格式化的 JSON 形式展示,方便查看和分析接口返回的数据结构</p></div></div></div></div></main></div></div>
</template><style>
body {min-height: 100vh;
}#app {min-height: 100vh;
}body::-webkit-scrollbar {width: 15px;
}body::-webkit-scrollbar-track {background: #f1f5f9;border-radius: 8px;
}body::-webkit-scrollbar-thumb {background: #6366f1;border-radius: 8px;border: 2px solid #f1f5f9;
}body::-webkit-scrollbar-thumb:hover {background: #4f46e5;
}
</style>

SpringBoot/src/main/java/com/boot/service/ApiService.java

package com.boot.service;import com.boot.model.ApiRequest;
import com.boot.model.ApiResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;import java.util.Map;@Service
public class ApiService {private final RestTemplate restTemplate;public ApiService() {this.restTemplate = new RestTemplate();}public ApiResponse executeRequest(ApiRequest request) {long startTime = System.currentTimeMillis();ApiResponse response = new ApiResponse();try {// 构建请求头HttpHeaders headers = new HttpHeaders();if (request.getHeaders() != null) {request.getHeaders().forEach(headers::add);}// 构建URL(包含查询参数)UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(request.getUrl());if (request.getQueryParams() != null) {for (Map.Entry<String, String> entry : request.getQueryParams().entrySet()) {builder.queryParam(entry.getKey(), entry.getValue());}}// 构建请求实体HttpEntity<?> httpEntity = new HttpEntity<>(request.getBody(), headers);// 不要对 URL 组件进行编码String finalUrl = builder.build(false).toUri().toString();// 修改执行请求部分,使用 String.class 接收响应ResponseEntity<String> responseEntity = restTemplate.exchange(finalUrl,HttpMethod.valueOf(request.getMethod().toUpperCase()),httpEntity,String.class);// 设置响应信息response.setStatus(responseEntity.getStatusCodeValue());// 尝试将响应转换为 JSON 对象String responseBody = responseEntity.getBody();try {ObjectMapper mapper = new ObjectMapper();Object jsonData = mapper.readValue(responseBody, Object.class);response.setData(jsonData);} catch (JsonProcessingException e) {// 如果不是 JSON 格式,直接返回字符串response.setData(responseBody);}response.setResponseTime(System.currentTimeMillis() - startTime);response.setContentLength(responseEntity.getHeaders().getContentLength() != -1? responseEntity.getHeaders().getContentLength() + " bytes": "unknown");} catch (Exception e) {e.printStackTrace();response.setStatus(500);String message = e.getMessage();if (message.contains("[")) {response.setData(message.substring(message.indexOf("[")));}response.setResponseTime(System.currentTimeMillis() - startTime);}return response;}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词