java
复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API请求工具 - Postman风格</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Roboto', sans-serif;
}
:root {
--primary: #ff6b35;
--secondary: #2d5d7b;
--dark: #1e2a38;
--light: #f5f7fa;
--success: #4caf50;
--warning: #ff9800;
--danger: #f44336;
--gray: #78909c;
}
body {
background-color: #f0f2f5;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background: linear-gradient(135deg, var(--dark), var(--secondary));
color: white;
padding: 15px 0;
margin-bottom: 30px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
}
.logo {
display: flex;
align-items: center;
gap: 10px;
}
.logo i {
font-size: 28px;
color: var(--primary);
}
.logo h1 {
font-size: 24px;
font-weight: 700;
}
.request-controls {
display: flex;
gap: 20px;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 25px;
}
.method-select {
width: 120px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
font-weight: 500;
color: var(--secondary);
}
.url-input {
flex: 1;
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.send-btn {
padding: 10px 25px;
background: linear-gradient(135deg, var(--primary), #ff5722);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(255, 107, 53, 0.3);
}
.send-btn:hover {
background: linear-gradient(135deg, #ff5722, var(--primary));
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(255, 107, 53, 0.4);
}
.send-btn:active {
transform: translateY(0);
}
.section {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 25px;
overflow: hidden;
}
.section-header {
background-color: var(--light);
padding: 12px 20px;
font-weight: 600;
color: var(--secondary);
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
gap: 8px;
}
.section-content {
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--dark);
}
textarea {
width: 100%;
min-height: 150px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
font-family: monospace;
resize: vertical;
}
.response-info {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.status-badge {
padding: 6px 12px;
background-color: var(--success);
color: white;
border-radius: 4px;
font-weight: 600;
font-size: 14px;
}
.size-info {
color: var(--gray);
font-size: 14px;
display: flex;
align-items: center;
gap: 5px;
}
.response-view {
position: relative;
border: 1px solid #eee;
border-radius: 4px;
overflow: hidden;
}
.response-content {
padding: 15px;
font-family: monospace;
font-size: 14px;
background-color: #fafafa;
white-space: pre-wrap;
word-break: break-all;
min-height: 200px;
max-height: 500px;
overflow-y: auto;
}
.copy-btn {
position: absolute;
top: 10px;
right: 10px;
padding: 6px 10px;
background-color: white;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
color: var(--gray);
transition: all 0.2s;
}
.copy-btn:hover {
background-color: var(--light);
color: var(--secondary);
}
.input-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.input-row .form-group {
flex: 1;
margin-bottom: 0;
}
.input-row input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.instructions {
background-color: #fff8e1;
padding: 15px;
border-radius: 6px;
font-size: 14px;
color: #333;
margin-top: 25px;
border-left: 4px solid var(--warning);
}
.instructions h3 {
margin-bottom: 10px;
color: var(--dark);
}
.instructions ul {
margin-left: 20px;
}
.instructions li {
margin-bottom: 8px;
}
@media (max-width: 768px) {
.header-content {
flex-direction: column;
gap: 15px;
text-align: center;
}
.request-controls {
flex-direction: column;
}
.input-row {
flex-direction: column;
gap: 10px;
}
}
.footer {
text-align: center;
padding: 20px;
color: var(--gray);
font-size: 14px;
margin-top: 30px;
}
</style>
</head>
<body>
<header>
<div class="header-content">
<div class="logo">
<i class="fas fa-bolt"></i>
<h1>API请求工具</h1>
</div>
<p>模拟Postman的HTTP请求测试工具</p>
</div>
</header>
<div class="container">
<div class="request-controls">
<select class="method-select" id="method-select">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
<option value="PATCH">PATCH</option>
</select>
<input
type="url"
class="url-input"
id="url-input"
placeholder="输入请求URL..."
value=""
>
<button class="send-btn" id="send-btn">
<i class="fas fa-paper-plane"></i> 发送请求
</button>
</div>
<div class="section">
<div class="section-header">
<i class="fas fa-edit"></i>
<span>请求头</span>
</div>
<div class="section-content">
<div class="input-row">
<div class="form-group">
<label>Header Key</label>
<input type="text" id="header-key" placeholder="例如: Content-Type">
</div>
<div class="form-group">
<label>Header Value</label>
<input type="text" id="header-value" placeholder="例如: application/json">
</div>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<i class="fas fa-code"></i>
<span>请求体 (JSON格式)</span>
</div>
<div class="section-content">
<div class="form-group">
<textarea id="request-body" placeholder='{
"key1": "value1",
"key2": "value2"
}'></textarea>
</div>
</div>
</div>
<div class="section">
<div class="section-header">
<i class="fas fa-reply"></i>
<span>响应内容</span>
</div>
<div class="section-content">
<div class="response-info">
<div class="status-badge" id="status-code">等待请求...</div>
<div class="size-info" id="response-size">
<i class="fas fa-weight-hanging"></i> <span>0 Bytes</span>
</div>
</div>
<div class="response-view">
<div class="response-content" id="response-content">
// 请求结果将显示在这里
</div>
<button class="copy-btn" id="copy-btn">
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
</div>
<div class="instructions">
<h3><i class="fas fa-info-circle"></i> 使用说明</h3>
<ul>
<li>选择请求方法(默认为GET),输入URL地址</li>
<li>可以添加自定义请求头(可选)</li>
<li>对于POST/PUT请求,可以在请求体区域输入JSON格式数据</li>
<li>点击"发送请求"按钮提交API请求</li>
<li>响应区域将显示状态码、响应大小和返回内容</li>
<li>使用"复制"按钮可以快速复制响应内容</li>
</ul>
</div>
</div>
<div class="footer">
<p>© 2023 API请求工具 | 模拟Postman风格接口测试工具</p>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const sendBtn = document.getElementById('send-btn');
const copyBtn = document.getElementById('copy-btn');
const urlInput = document.getElementById('url-input');
const methodSelect = document.getElementById('method-select');
const requestBody = document.getElementById('request-body');
const responseContent = document.getElementById('response-content');
const statusCode = document.getElementById('status-code');
const responseSize = document.querySelector('#response-size span');
const headerKey = document.getElementById('header-key');
const headerValue = document.getElementById('header-value');
// 初始禁用请求体的提示(仅用于GET请求)
updateBodyPlaceholder();
// 监听方法选择变化
methodSelect.addEventListener('change', updateBodyPlaceholder);
function updateBodyPlaceholder() {
if(methodSelect.value === 'GET') {
requestBody.placeholder = "GET请求不需要请求体";
requestBody.disabled = true;
} else {
requestBody.placeholder = '{\n "key1": "value1",\n "key2": "value2"\n}';
requestBody.disabled = false;
}
}
// 发送请求
sendBtn.addEventListener('click', async function() {
const method = methodSelect.value;
const url = urlInput.value;
const headers = {};
// 如果有请求头
if(headerKey.value && headerValue.value) {
headers[headerKey.value] = headerValue.value;
}
// 处理请求体
let data = null;
if(method !== 'GET' && requestBody.value.trim() !== '') {
try {
data = JSON.parse(requestBody.value);
} catch (e) {
displayErrorResponse("请求体JSON格式错误: " + e.message);
return;
}
}
// 显示加载状态
responseContent.textContent = "发送请求中...";
statusCode.textContent = "请求中...";
statusCode.style.backgroundColor = '#ff9800';
sendBtn.disabled = true;
sendBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 发送中...';
try {
// 使用axios发送请求
const response = await axios({
method: method,
url: url,
data: data,
headers: headers
});
// 更新UI
updateResponseUI(response);
} catch (error) {
// 处理错误
if (error.response) {
// 服务器返回了错误的响应状态(4xx, 5xx)
updateResponseUI(error.response);
} else {
// 请求未发出(网络错误等)
displayErrorResponse("请求失败: " + error.message);
}
} finally {
sendBtn.disabled = false;
sendBtn.innerHTML = '<i class="fas fa-paper-plane"></i> 发送请求';
}
});
function updateResponseUI(response) {
// 显示响应状态
const status = response.status;
statusCode.textContent = `${status} ${response.statusText}`;
// 根据状态码设置颜色
if (status >= 200 && status < 300) {
statusCode.style.backgroundColor = '#4caf50'; // 成功 - 绿色
} else if (status >= 300 && status < 400) {
statusCode.style.backgroundColor = '#ff9800'; // 重定向 - 橙色
} else if (status >= 400 && status < 500) {
statusCode.style.backgroundColor = '#f44336'; // 客户端错误 - 红色
} else if (status >= 500) {
statusCode.style.backgroundColor = '#9c27b0'; // 服务端错误 - 紫色
}
// 显示响应大小
let size = 0;
if (response.data) {
size = JSON.stringify(response.data).length;
}
// 转换为更易读的大小
let readableSize = size + " Bytes";
if (size > 1024) {
readableSize = (size / 1024).toFixed(2) + " KB";
}
responseSize.textContent = readableSize;
// 格式化JSON响应
let formattedResponse;
if (typeof response.data === 'object') {
formattedResponse = JSON.stringify(response.data, null, 2);
} else if (typeof response.data === 'string') {
try {
// 尝试解析为JSON
formattedResponse = JSON.stringify(JSON.parse(response.data), null, 2);
} catch (e) {
formattedResponse = response.data;
}
} else {
formattedResponse = response.data;
}
responseContent.textContent = formattedResponse;
}
function displayErrorResponse(message) {
statusCode.textContent = "错误";
statusCode.style.backgroundColor = '#f44336';
responseContent.textContent = message;
}
// 复制响应内容
copyBtn.addEventListener('click', function() {
const textToCopy = responseContent.textContent;
navigator.clipboard.writeText(textToCopy).then(() => {
// 显示复制成功消息
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
setTimeout(() => {
copyBtn.innerHTML = originalText;
}, 2000);
});
});
});
</script>
</body>
</html>