581 lines
23 KiB
HTML
581 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>ASP.NET Core Minimal API CRUD Test</title>
|
||
<style>
|
||
body {
|
||
font-family: Arial, sans-serif;
|
||
margin: 20px;
|
||
background-color: #f4f4f4;
|
||
color: #333;
|
||
}
|
||
h1, h2, h3 {
|
||
color: #0056b3;
|
||
}
|
||
.container {
|
||
display: flex;
|
||
gap: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.entity-section {
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
padding: 20px;
|
||
flex: 1;
|
||
min-width: 450px;
|
||
box-sizing: border-box;
|
||
}
|
||
.operation-section {
|
||
padding: 15px;
|
||
border: 1px solid #eee;
|
||
border-radius: 5px;
|
||
margin-bottom: 15px;
|
||
background-color: #f9f9f9;
|
||
}
|
||
.operation-section h4 {
|
||
margin-top: 0;
|
||
color: #333;
|
||
}
|
||
label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: bold;
|
||
}
|
||
input[type="text"],
|
||
input[type="number"],
|
||
input[type="email"],
|
||
input[type="password"],
|
||
input[type="checkbox"],
|
||
textarea {
|
||
width: calc(100% - 22px);
|
||
padding: 8px;
|
||
margin-bottom: 10px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 4px;
|
||
box-sizing: border-box;
|
||
}
|
||
input[type="checkbox"] {
|
||
width: auto;
|
||
margin-right: 5px;
|
||
}
|
||
button {
|
||
background-color: #007bff;
|
||
color: white;
|
||
padding: 10px 15px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
transition: background-color 0.2s ease;
|
||
margin-right: 10px;
|
||
}
|
||
button:hover {
|
||
background-color: #0056b3;
|
||
}
|
||
.result-box {
|
||
background-color: #e9ecef;
|
||
border: 1px solid #ced4da;
|
||
padding: 15px;
|
||
margin-top: 15px;
|
||
border-radius: 5px;
|
||
white-space: pre-wrap;
|
||
font-family: monospace;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
}
|
||
.error {
|
||
color: red;
|
||
font-weight: bold;
|
||
}
|
||
.success {
|
||
color: green;
|
||
font-weight: bold;
|
||
}
|
||
.loading {
|
||
color: gray;
|
||
font-style: italic;
|
||
}
|
||
ul {
|
||
list-style-type: none;
|
||
padding: 0;
|
||
}
|
||
li {
|
||
background-color: #f0f0f0;
|
||
margin-bottom: 5px;
|
||
padding: 8px;
|
||
border-radius: 3px;
|
||
border-left: 5px solid #007bff;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>ASP.NET Core Minimal API CRUD 测试</h1>
|
||
<p>确保您的 ASP.NET Core 应用正在运行,并监听以下基准 URL。</</p>
|
||
|
||
<div class="container">
|
||
<!-- Todo Section -->
|
||
<div class="entity-section">
|
||
<h2>待办事项 (Todos) CRUD</h2>
|
||
|
||
<!-- 获取所有 Todo -->
|
||
<div class="operation-section">
|
||
<h4>获取所有待办事项</h4>
|
||
<button onclick="getAllTodos()">获取所有 Todo</button>
|
||
<div id="allTodosResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 根据 ID 获取 Todo -->
|
||
<div class="operation-section">
|
||
<h4>根据 ID 获取待办事项</h4>
|
||
<label for="getTodoId">待办事项 ID:</label>
|
||
<input type="number" id="getTodoId" value="1">
|
||
<button onclick="getTodoById()">获取 Todo</button>
|
||
<div id="singleTodoResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 创建 Todo -->
|
||
<div class="operation-section">
|
||
<h4>创建待办事项</h4>
|
||
<label for="createTodoTitle">标题:</label>
|
||
<input type="text" id="createTodoTitle" placeholder="新的待办事项标题">
|
||
<label for="createTodoIsCompleted">已完成:</label>
|
||
<input type="checkbox" id="createTodoIsCompleted">
|
||
<label for="createTodoUserId">用户ID (关联的用户):</label>
|
||
<input type="number" id="createTodoUserId" value="1" placeholder="例如: 1">
|
||
<button onclick="createTodo()">创建 Todo</button>
|
||
<div id="createTodoResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 更新 Todo -->
|
||
<div class="operation-section">
|
||
<h4>更新待办事项</h4>
|
||
<label for="updateTodoId">待办事项 ID:</label>
|
||
<input type="number" id="updateTodoId" value="1">
|
||
<label for="updateTodoTitle">新标题:</label>
|
||
<input type="text" id="updateTodoTitle" placeholder="更新后的待办事项标题">
|
||
<label for="updateTodoIsCompleted">已完成:</label>
|
||
<input type="checkbox" id="updateTodoIsCompleted">
|
||
<label for="updateTodoUserId">用户ID (关联的用户):</label>
|
||
<input type="number" id="updateTodoUserId" value="1" placeholder="例如: 1">
|
||
<button onclick="updateTodo()">更新 Todo</button>
|
||
<div id="updateTodoResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 删除 Todo -->
|
||
<div class="operation-section">
|
||
<h4>删除待办事项</h4>
|
||
<label for="deleteTodoId">待办事项 ID:</label>
|
||
<input type="number" id="deleteTodoId" value="1">
|
||
<button onclick="deleteTodo()">删除 Todo</button>
|
||
<div id="deleteTodoResult" class="result-box"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- User Section -->
|
||
<div class="entity-section">
|
||
<h2>用户 (Users) CRUD</h2>
|
||
|
||
<!-- 获取所有 User -->
|
||
<div class="operation-section">
|
||
<h4>获取所有用户</h4>
|
||
<button onclick="getAllUsers()">获取所有 User</button>
|
||
<div id="allUsersResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 根据 ID 获取 User -->
|
||
<div class="operation-section">
|
||
<h4>根据 ID 获取用户</h4>
|
||
<label for="getUserId">用户 ID:</label>
|
||
<input type="number" id="getUserId" value="1">
|
||
<button onclick="getUserById()">获取 User</button>
|
||
<div id="singleUserResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 用户验证/登录 (使用 PATCH 方法) -->
|
||
<div class="operation-section">
|
||
<h4>用户验证 (Check User Credentials)</h4>
|
||
<label for="verifyUsername">用户名:</label>
|
||
<input type="text" id="verifyUsername" placeholder="要验证的用户名">
|
||
<label for="verifyPasswordHash">密码哈希:</label>
|
||
<input type="password" id="verifyPasswordHash" placeholder="对应用户的密码哈希">
|
||
<button onclick="verifyUserCredentials()">验证用户</button>
|
||
<div id="verifyUserResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 创建 User -->
|
||
<div class="operation-section">
|
||
<h4>创建用户</h4>
|
||
<label for="createUsername">用户名:</label>
|
||
<input type="text" id="createUsername" placeholder="新用户名">
|
||
<label for="createUserEmail">邮箱:</label>
|
||
<input type="email" id="createUserEmail" placeholder="user@example.com">
|
||
<label for="createUserPasswordHash">密码哈希 (例如: plainTextPassword的哈希值):</label>
|
||
<input type="password" id="createUserPasswordHash" placeholder="PasswordHash">
|
||
<button onclick="createUser()">创建 User</button>
|
||
<div id="createUserResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 更新 User -->
|
||
<div class="operation-section">
|
||
<h4>更新用户</h4>
|
||
<label for="updateUserId">用户 ID:</label>
|
||
<input type="number" id="updateUserId" value="1">
|
||
<label for="updateUsername">新用户名:</label>
|
||
<input type="text" id="updateUsername" placeholder="更新后的用户名">
|
||
<label for="updateUserEmail">新邮箱:</label>
|
||
<input type="email" id="updateUserEmail" placeholder="updated@example.com">
|
||
<label for="updateUserPasswordHash">新密码哈希:</label>
|
||
<input type="password" id="updateUserPasswordHash" placeholder="UpdatedPasswordHash">
|
||
<button onclick="updateUser()">更新 User</button>
|
||
<div id="updateUserResult" class="result-box"></div>
|
||
</div>
|
||
|
||
<!-- 删除 User -->
|
||
<div class="operation-section">
|
||
<h4>删除用户</h4>
|
||
<label for="deleteUserId">用户 ID:</label>
|
||
<input type="number" id="deleteUserId" value="1">
|
||
<button onclick="deleteUser()">删除 User</button>
|
||
<div id="deleteUserResult" class="result-box"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// === 配置您的 API 基础 URL ===
|
||
// 如果您的 API 运行在其他端口或地址,请修改此变量
|
||
const BASE_URL = 'http://localhost:5071';
|
||
// 对于 HTTPS,可能需要改为 'https://localhost:5001'
|
||
|
||
// === 辅助函数 ===
|
||
async function fetchData(url, method = 'GET', data = null) {
|
||
const options = {
|
||
method: method,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
};
|
||
if (data) {
|
||
options.body = JSON.stringify(data);
|
||
}
|
||
try {
|
||
const response = await fetch(url, options);
|
||
let result;
|
||
// 尝试解析JSON响应,但对于204 No Content等情况,可能没有响应体
|
||
if (response.headers.get("content-type")?.includes("application/json")) {
|
||
result = await response.json();
|
||
} else {
|
||
result = await response.text(); // 如果不是 JSON,尝试获取文本
|
||
}
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP 错误! Status: ${response.status}, Message: ${result}`);
|
||
}
|
||
return result;
|
||
} catch (error) {
|
||
console.error('Fetch error:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
function displayResult(elementId, data, isError = false) {
|
||
const resultBox = document.getElementById(elementId);
|
||
resultBox.innerHTML = '';
|
||
if (isError) {
|
||
resultBox.classList.add('error');
|
||
resultBox.textContent = `Error: ${data}`;
|
||
} else {
|
||
resultBox.classList.remove('error');
|
||
resultBox.classList.add('success');
|
||
if (typeof data === 'object') {
|
||
resultBox.textContent = JSON.stringify(data, null, 2);
|
||
} else {
|
||
resultBox.textContent = data;
|
||
}
|
||
}
|
||
resultBox.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}
|
||
|
||
// === Todo Functions ===
|
||
|
||
async function getAllTodos() {
|
||
const resultBox = document.getElementById('allTodosResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '加载中...';
|
||
try {
|
||
const todos = await fetchData(`${BASE_URL}/todos`);
|
||
displayResult('allTodosResult', todos);
|
||
} catch (error) {
|
||
displayResult('allTodosResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function getTodoById() {
|
||
const id = document.getElementById('getTodoId').value;
|
||
if (!id) {
|
||
displayResult('singleTodoResult', '请输入待办事项 ID。', true);
|
||
return;
|
||
}
|
||
const resultBox = document.getElementById('singleTodoResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '加载中...';
|
||
try {
|
||
const todo = await fetchData(`${BASE_URL}/todos/${id}`);
|
||
displayResult('singleTodoResult', todo);
|
||
} catch (error) {
|
||
displayResult('singleTodoResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function createTodo() {
|
||
const title = document.getElementById('createTodoTitle').value;
|
||
const isCompleted = document.getElementById('createTodoIsCompleted').checked;
|
||
const userId = parseInt(document.getElementById('createTodoUserId').value);
|
||
|
||
if (!title) {
|
||
displayResult('createTodoResult', '标题不能为空。', true);
|
||
return;
|
||
}
|
||
if (isNaN(userId)) {
|
||
displayResult('createTodoResult', '用户ID必须是数字。', true);
|
||
return;
|
||
}
|
||
|
||
const newTodo = {
|
||
title: title,
|
||
isCompleted: isCompleted,
|
||
userId: userId
|
||
};
|
||
const resultBox = document.getElementById('createTodoResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '创建中...';
|
||
try {
|
||
const createdTodo = await fetchData(`${BASE_URL}/todos`, 'POST', newTodo);
|
||
displayResult('createTodoResult', createdTodo);
|
||
// 刷新所有 Todo 列表
|
||
getAllTodos();
|
||
} catch (error) {
|
||
displayResult('createTodoResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function updateTodo() {
|
||
const id = document.getElementById('updateTodoId').value;
|
||
const title = document.getElementById('updateTodoTitle').value;
|
||
const isCompleted = document.getElementById('updateTodoIsCompleted').checked;
|
||
const userId = parseInt(document.getElementById('updateTodoUserId').value);
|
||
|
||
if (!id) {
|
||
displayResult('updateTodoResult', '请输入待办事项 ID。', true);
|
||
return;
|
||
}
|
||
if (!title) {
|
||
displayResult('updateTodoResult', '标题不能为空。', true);
|
||
return;
|
||
}
|
||
if (isNaN(userId)) {
|
||
displayResult('updateTodoResult', '用户ID必须是数字。', true);
|
||
return;
|
||
}
|
||
|
||
const updatedTodo = {
|
||
id: parseInt(id), // ID 也需要包含在 PUT 请求体中
|
||
title: title,
|
||
isCompleted: isCompleted,
|
||
userId: userId
|
||
};
|
||
const resultBox = document.getElementById('updateTodoResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '更新中...';
|
||
try {
|
||
await fetchData(`${BASE_URL}/todos/${id}`, 'PUT', updatedTodo);
|
||
displayResult('updateTodoResult', `待办事项 (ID: ${id}) 更新成功。`);
|
||
getTodoById(); // 刷新单个Todo
|
||
getAllTodos(); // 刷新所有Todo
|
||
} catch (error) {
|
||
displayResult('updateTodoResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function deleteTodo() {
|
||
const id = document.getElementById('deleteTodoId').value;
|
||
if (!id) {
|
||
displayResult('deleteTodoResult', '请输入待办事项 ID。', true);
|
||
return;
|
||
}
|
||
const resultBox = document.getElementById('deleteTodoResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '删除中...';
|
||
try {
|
||
const response = await fetchData(`${BASE_URL}/todos/${id}`, 'DELETE');
|
||
displayResult('deleteTodoResult', response);
|
||
getAllTodos(); // 刷新所有Todo
|
||
} catch (error) {
|
||
displayResult('deleteTodoResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
// === User Functions ===
|
||
|
||
async function getAllUsers() {
|
||
const resultBox = document.getElementById('allUsersResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '加载中...';
|
||
try {
|
||
const users = await fetchData(`${BASE_URL}/users`);
|
||
displayResult('allUsersResult', users);
|
||
} catch (error) {
|
||
displayResult('allUsersResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function getUserById() {
|
||
const id = document.getElementById('getUserId').value;
|
||
if (!id) {
|
||
displayResult('singleUserResult', '请输入用户 ID。', true);
|
||
return;
|
||
}
|
||
const resultBox = document.getElementById('singleUserResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '加载中...';
|
||
try {
|
||
const user = await fetchData(`${BASE_URL}/users/${id}`);
|
||
displayResult('singleUserResult', user);
|
||
} catch (error) {
|
||
displayResult('singleUserResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function createUser() {
|
||
const username = document.getElementById('createUsername').value;
|
||
const email = document.getElementById('createUserEmail').value;
|
||
const passwordHash = document.getElementById('createUserPasswordHash').value;
|
||
|
||
if (!username || !email || !passwordHash) {
|
||
displayResult('createUserResult', '用户名、邮箱和密码哈希不能为空。', true);
|
||
return;
|
||
}
|
||
|
||
const newUser = {
|
||
username: username,
|
||
email: email,
|
||
passwordHash: passwordHash
|
||
};
|
||
const resultBox = document.getElementById('createUserResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '创建中...';
|
||
try {
|
||
const createdUser = await fetchData(`${BASE_URL}/users`, 'POST', newUser);
|
||
displayResult('createUserResult', createdUser);
|
||
getAllUsers(); // 刷新所有用户列表
|
||
} catch (error) {
|
||
displayResult('createUserResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
// PACTH /users - 验证用户凭据
|
||
async function verifyUserCredentials() {
|
||
const username = document.getElementById('verifyUsername').value;
|
||
const passwordHash = document.getElementById('verifyPasswordHash').value;
|
||
|
||
if (!username || !passwordHash) {
|
||
displayResult('verifyUserResult', '用户名和密码哈希不能为空。', true);
|
||
return;
|
||
}
|
||
|
||
const userToVerify = {
|
||
// PATCH 请求体中通常不包含 ID,因为您的 API 是通过 Username 来查找
|
||
// 但如果需要,也可以写上 Id: 0 或其他占位符
|
||
username: username,
|
||
passwordHash: passwordHash
|
||
};
|
||
|
||
const resultBox = document.getElementById('verifyUserResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '验证中...';
|
||
|
||
try {
|
||
// 使用 PATCH 方法发送请求
|
||
const response = await fetchData(`${BASE_URL}/users`, 'PATCH', userToVerify);
|
||
displayResult('verifyUserResult', response);
|
||
} catch (error) {
|
||
// 如果 API 返回的是 BadRequest (400),fetchData 会抛出异常
|
||
// 异常的 message 应该包含 API 返回的错误信息
|
||
displayResult('verifyUserResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function updateUser() {
|
||
const id = document.getElementById('updateUserId').value;
|
||
const username = document.getElementById('updateUsername').value;
|
||
const email = document.getElementById('updateUserEmail').value;
|
||
const passwordHash = document.getElementById('updateUserPasswordHash').value;
|
||
|
||
if (!id) {
|
||
displayResult('updateUserResult', '请输入用户 ID。', true);
|
||
return;
|
||
}
|
||
if (!username || !email || !passwordHash) {
|
||
displayResult('updateUserResult', '用户名、邮箱和密码哈希不能为空。', true);
|
||
return;
|
||
}
|
||
|
||
const updatedUser = {
|
||
id: parseInt(id), // ID 也需要包含在 PUT 请求体中
|
||
username: username,
|
||
email: email,
|
||
passwordHash: passwordHash
|
||
};
|
||
const resultBox = document.getElementById('updateUserResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '更新中...';
|
||
try {
|
||
await fetchData(`${BASE_URL}/users/${id}`, 'PUT', updatedUser);
|
||
displayResult('updateUserResult', `用户 (ID: ${id}) 更新成功。`);
|
||
getUserById(); // 刷新单个用户
|
||
getAllUsers(); // 刷新所有用户
|
||
} catch (error) {
|
||
displayResult('updateUserResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
async function deleteUser() {
|
||
const id = document.getElementById('deleteUserId').value;
|
||
if (!id) {
|
||
displayResult('deleteUserResult', '请输入用户 ID。', true);
|
||
return;
|
||
}
|
||
const resultBox = document.getElementById('deleteUserResult');
|
||
resultBox.classList.remove('success', 'error');
|
||
resultBox.classList.add('loading');
|
||
resultBox.textContent = '删除中...';
|
||
try {
|
||
const response = await fetchData(`${BASE_URL}/users/${id}`, 'DELETE');
|
||
displayResult('deleteUserResult', response);
|
||
getAllUsers(); // 刷新所有用户列表
|
||
} catch (error) {
|
||
displayResult('deleteUserResult', error.message, true);
|
||
}
|
||
}
|
||
|
||
// 页面加载完成后自动获取所有待办事项和用户
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
getAllTodos();
|
||
getAllUsers();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |