Vue与ASP.NET Core Web Api设置localhost与本地ip地址皆可访问

Vue的设置

我们创建并启动一个Vue项目,如下所示:

image-20240724091722654

打开cmd,输入ipconfig查询本地ip地址:

image-20240724092321646

想通过本地ip地址访问,把localhost改成本地ip地址,发现打不开:

image-20240724092439478

这是因为Vue项目默认只有localhost,查看启动终端:

image-20240724092543635

这时候就需要进行配置,打开package.json,在scripts下的dev中加上–host 0.0.0.0:

image-20240724092807126

再次启动,查看终端:

image-20240724092941670

这样子就可以了,通过本地ip访问服务,如下所示:

image-20240724093139130

ASP.NET Core Web Api的设置

我们创建并启动一个Web Api,默认也是localhost,如下所示:

image-20240724093829661

直接本地ip地址访问,也是打不开如下所示:

image-20240724094025454

在Program中加上一行

 builder.WebHost.UseUrls("https://*:7101");

即可,如下所示:

image-20240724094254887

再次启动,现在通过localhost与本地ip地址都可访问服务了,如下所示:

image-20240724095630573

<think>我们将使用Vue的axios库来调用API。如果还没有安装axios,需要先安装。 步骤: 1. 安装axios 2.Vue组件中引入axios 3. 发送POST请求到指定接口 4. 处理响应和错误 注意:由于接口地址是https,且使用localhost,需要确保后端服务已启动且支持HTTPS(或者在前端配置代理以避免跨域问题,但这里我们先按直接调用写,然后讨论跨域问题)。 我们将创建一个登录方法,当点击按钮时触发,发送PWD=123456到接口。 由于接口可能需要表单数据(application/x-www-form-urlencoded)或JSON格式,这里我们根据接口要求选择。假设接口要求表单格式,我们将使用URLSearchParams来构建参数。 如果接口要求JSON格式,则直接发送JSON对象。 我们这里先按照表单格式发送。 另外,注意如果后端使用自签名证书,可能会遇到证书问题,在开发环境中可以暂时配置axios忽略证书(仅限开发环境,生产环境不可用此配置)。 但是,由于我们的接口是localhost,且开发环境下通常使用HTTP,这里我们假设接口是HTTP(因为HTTPS的localhost需要证书,如果没有配置会报错)。如果后端是HTTP,则地址应为http://localhost:5000。 因此,我们先假设接口是HTTP,地址为http://localhost:5000/api/admin/login。 如果确实是HTTPS且证书有效,则使用https地址。 考虑到安全,生产环境必须使用HTTPS。 现在,我们写代码:</think># 在 Vue 中调用 Web API 接口的完整指南 我将为您提供一个完整的解决方案,展示如何在 Vue 页面中调用 `https://localhost:5000/api/admin/login` 接口并传递 `PWD=123456` 参数。这个方案包含完整的 Vue 组件实现、错误处理、状态管理和安全措施。 ## 完整实现方案 ### 1. 安装必要依赖 ```bash npm install axios vue-axios ``` ### 2. 创建 API 服务文件 ```javascript // src/services/api.js import axios from 'axios'; // 创建 axios 实例 const apiClient = axios.create({ baseURL: 'https://localhost:5000/api', timeout: 10000, // 10秒超时 headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } }); // 请求拦截器(添加认证信息等) apiClient.interceptors.request.use(config => { // 可以在这里添加全局请求头,如认证令牌 const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => { return Promise.reject(error); }); // 响应拦截器(处理全局错误) apiClient.interceptors.response.use(response => { return response; }, error => { // 统一处理错误 if (error.response) { console.error('API Error:', error.response.status, error.response.data); } else if (error.request) { console.error('No response received:', error.request); } else { console.error('Request setup error:', error.message); } return Promise.reject(error); }); export default { // 登录方法 async login(password) { try { // 使用 URLSearchParams 发送表单格式数据 const params = new URLSearchParams(); params.append('PWD', password); const response = await apiClient.post('/admin/login', params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); return response.data; } catch (error) { throw this.handleLoginError(error); } }, // 错误处理方法 handleLoginError(error) { if (!error.response) { return { message: '无法连接到服务器,请检查网络连接' }; } const status = error.response.status; switch (status) { case 400: return { message: '请求参数错误' }; case 401: return { message: '密码错误或权限不足' }; case 403: return { message: '访问被拒绝' }; case 404: return { message: 'API接口不存在' }; case 500: return { message: '服务器内部错误' }; default: return { message: `未知错误: ${status}` }; } } }; ``` ### 3. 创建登录页面组件 ```vue <!-- src/views/LoginPage.vue --> <template> <div class="login-container"> <div class="login-card"> <h2 class="login-title">管理员登录</h2> <form @submit.prevent="handleLogin" class="login-form"> <div class="form-group"> <label for="password">密码</label> <input type="password" id="password" v-model="password" placeholder="请输入密码" required class="form-input" :class="{ 'input-error': errors.password }" /> <div v-if="errors.password" class="error-message">{{ errors.password }}</div> </div> <button type="submit" class="login-button" :disabled="isLoading" > <span v-if="!isLoading">登录</span> <span v-else>登录中...</span> </button> </form> <div v-if="apiMessage" class="api-response" :class="{'success': loginSuccess, 'error': !loginSuccess}"> {{ apiMessage }} </div> <div v-if="loginSuccess" class="success-actions"> <button @click="goToDashboard" class="action-button">进入管理面板</button> </div> </div> <div v-if="isLoading" class="loading-overlay"> <div class="spinner"></div> </div> </div> </template> <script> import api from '@/services/api'; export default { name: 'LoginPage', data() { return { password: '123456', // 默认密码 isLoading: false, loginSuccess: false, apiMessage: '', errors: { password: '' } }; }, methods: { validateForm() { let isValid = true; this.errors = { password: '' }; if (!this.password) { this.errors.password = '密码不能为空'; isValid = false; } else if (this.password.length < 6) { this.errors.password = '密码长度至少为6位'; isValid = false; } return isValid; }, async handleLogin() { if (!this.validateForm()) return; this.isLoading = true; this.apiMessage = ''; try { // 调用API服务 const response = await api.login(this.password); // 处理成功响应 this.loginSuccess = true; this.apiMessage = '登录成功!'; // 存储认证信息 localStorage.setItem('auth_token', response.token); localStorage.setItem('user_role', 'admin'); console.log('登录响应:', response); } catch (error) { // 处理错误 this.loginSuccess = false; this.apiMessage = error.message || '登录失败,请重试'; console.error('登录错误:', error); } finally { this.isLoading = false; } }, goToDashboard() { this.$router.push('/admin/dashboard'); } } }; </script> <style scoped> .login-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); padding: 20px; } .login-card { background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); width: 100%; max-width: 450px; padding: 40px; position: relative; overflow: hidden; } .login-title { text-align: center; color: #2c3e50; margin-bottom: 30px; font-size: 28px; } .login-form { display: flex; flex-direction: column; gap: 20px; } .form-group { display: flex; flex-direction: column; gap: 8px; } label { font-weight: 500; color: #34495e; } .form-input { padding: 14px 16px; border: 1px solid #ddd; border-radius: 8px; font-size: 16px; transition: border-color 0.3s; } .form-input:focus { border-color: #3498db; outline: none; box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); } .input-error { border-color: #e74c3c; } .error-message { color: #e74c3c; font-size: 14px; margin-top: 4px; } .login-button { background: #3498db; color: white; border: none; border-radius: 8px; padding: 14px; font-size: 18px; font-weight: 500; cursor: pointer; transition: background 0.3s, transform 0.2s; margin-top: 10px; } .login-button:hover:not(:disabled) { background: #2980b9; transform: translateY(-2px); } .login-button:disabled { background: #bdc3c7; cursor: not-allowed; } .api-response { margin-top: 20px; padding: 15px; border-radius: 8px; text-align: center; font-weight: 500; } .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .success-actions { margin-top: 25px; display: flex; justify-content: center; } .action-button { background: #2ecc71; color: white; border: none; border-radius: 8px; padding: 12px 25px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background 0.3s; } .action-button:hover { background: #27ae60; } .loading-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255, 255, 255, 0.8); display: flex; justify-content: center; align-items: center; border-radius: 12px; z-index: 10; } .spinner { border: 4px solid rgba(0, 0, 0, 0.1); border-left-color: #3498db; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* 响应式设计 */ @media (max-width: 576px) { .login-card { padding: 25px; } .login-title { font-size: 24px; } } </style> ``` ### 4. 配置路由 ```javascript // src/router/index.js import { createRouter, createWebHistory } from 'vue-router'; import LoginPage from '@/views/LoginPage.vue'; const routes = [ { path: '/login', name: 'login', component: LoginPage }, // 其他路由... ]; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes }); export default router; ``` ### 5. 在 main.js 中配置 axios ```javascript // src/main.js import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; import axios from 'axios'; import VueAxios from 'vue-axios'; const app = createApp(App); app.use(router); app.use(VueAxios, axios); // 集成 axios // 设置全局 axios 配置 axios.defaults.withCredentials = true; // 跨域请求携带 cookie app.mount('#app'); ``` ## 关键实现说明 ### 1. API 调用核心逻辑 ```javascript // 使用 URLSearchParams 发送表单格式数据 const params = new URLSearchParams(); params.append('PWD', password); const response = await apiClient.post('/admin/login', params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); ``` 这里使用了 `URLSearchParams` 对象来构建表单格式的数据,确保参数以 `application/x-www-form-urlencoded` 格式发送,符合大多数 API 的要求。 ### 2. 错误处理机制 ```javascript handleLoginError(error) { if (!error.response) { return { message: '无法连接到服务器,请检查网络连接' }; } const status = error.response.status; switch (status) { case 400: return { message: '请求参数错误' }; case 401: return { message: '密码错误或权限不足' }; // ...其他状态码处理 default: return { message: `未知错误: ${status}` }; } } ``` 这个错误处理方法将 HTTP 状态码转换为用户友好的错误消息。 ### 3. 表单验证 ```javascript validateForm() { let isValid = true; this.errors = { password: '' }; if (!this.password) { this.errors.password = '密码不能为空'; isValid = false; } else if (this.password.length < 6) { this.errors.password = '密码长度至少为6位'; isValid = false; } return isValid; } ``` 在调用 API 前进行客户端验证,减少不必要的 API 请求。 ### 4. 加载状态管理 ```vue <button type="submit" class="login-button" :disabled="isLoading" > <span v-if="!isLoading">登录</span> <span v-else>登录中...</span> </button> <div v-if="isLoading" class="loading-overlay"> <div class="spinner"></div> </div> ``` 使用 `isLoading` 状态控制按钮禁用状态和显示加载指示器,提升用户体验。 ## 解决常见问题 ### 1. 跨域问题 (CORS) 如果遇到跨域问题,需要配置后端允许前端域名的请求: ```csharp // ASP.NET Core 示例 services.AddCors(options => { options.AddPolicy("AllowVueApp", builder => { builder.WithOrigins("http://localhost:8080", "https://yourdomain.com") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); ``` ### 2. HTTPS 证书问题 开发环境中如果使用自签名证书,需要配置 axios 忽略证书错误(仅限开发环境): ```javascript // 仅限开发环境使用 if (process.env.NODE_ENV === 'development') { process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } ``` ### 3. 安全增强 #### 添加 CSRF 保护 ```javascript // 在 axios 拦截器中添加 CSRF 令牌 apiClient.interceptors.request.use(config => { const csrfToken = getCookie('XSRF-TOKEN'); if (csrfToken) { config.headers['X-XSRF-TOKEN'] = csrfToken; } return config; }); ``` #### 密码安全处理 ```javascript // 使用加密库处理密码 import CryptoJS from 'crypto-js'; // 在发送前对密码进行哈希处理 const hashedPassword = CryptoJS.SHA256(password).toString(); params.append('PWD', hashedPassword); ``` ## 完整项目结构 ``` vue-api-project/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── router/ │ │ └── index.js │ ├── services/ │ │ └── api.js # API 服务 │ ├── views/ │ │ └── LoginPage.vue # 登录页面 │ ├── App.vue │ └── main.js ├── .env.development # 开发环境变量 ├── .env.production # 生产环境变量 ├── vue.config.js # Vue CLI 配置 └── package.json ``` ## 生产环境部署建议 1. **环境变量配置**: ```env # .env.production VITE_API_BASE_URL=https://api.yourdomain.com ``` 2. **API 服务配置**: ```javascript const apiClient = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || 'https://localhost:5000/api', // ... }); ``` 3. **安全头设置**: ```javascript // 在响应头中添加安全相关头 app.use(helmet()); ``` 4. **速率限制**: ```javascript // 添加 API 速率限制 app.use(rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 100 // 每个IP限制100次请求 })); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值