前传:浪花 - 根据标签搜索用户-CSDN博客
目录
一、完善后端搜索标签接口
二、前后端搜索标签接口的对接
1. 使用 Axios 发送请求
2. 解决跨域问题
3. Axios 请求传参序列化
4. 接收后端响应数据
5. 处理后端响应数据格式
6. 搜索结果为空的页面展示
附:解决跨域问题的几种方式
1. 方式一:修改域名和端口
2. 方式二:网关支持(Nginx)——添加允许跨域配置
3. 方式三:后端服务支持
一、完善后端搜索标签接口
1. 添加 Controller 层:调用 userService 中之前写好的 searchUsersByTags() 来根据前端传来的标签搜索过滤数据库中的用户
/**
* 根据标签搜索用户
* @param tagNameList 标签列表
* @return 搜索到的用户列表
*/
@GetMapping("/search/tags")
public BaseResponse<List<User>> searchUsersByTags(@RequestParam(required = false) List<String> tagNameList) {
if(CollectionUtils.isEmpty(tagNameList)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
List<User> userList = userService.searchUsersByTags(tagNameList);
return ResultUtils.success(userList);
}
2. 启动项目,测试接口
- 使用 Knife4j 接口文档传递参数进行接口调试
- 传递空的标签列表:后端做了请求参数是否为空的校验,参数为空抛出异常,全局异常处理器会进行统一处理
- 传递正确参数:后端根据参数列表(标签)过滤出来的用户列表数据
数据库暂时只有一个用户有标签信息,所以响应数据的用户列表只有一个用户
二、前后端搜索标签接口的对接
参考官方文档:axios中文网|axios API 中文文档 | axios (axios-js.com)
1. 使用 Axios 发送请求
-
整合 Axios
npm install axios
- 创建 Axios 实例并定义 baseURL:baseURL 作为请求的前缀,后续修改主机和端口号(项目部署上线时)只需要修改 baseURL 即可,不需要修改所有请求地址的前缀
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
- 定义全局请求拦截器和全局响应拦截器
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
- 发送搜索请求:点击搜索按钮进入搜索结果页面时,页面一加载就发送 GET 请求
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
- 引起跨域问题
Access to XMLHttpRequest at 'http://localhost:8080/api/user/search/tags' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
2. 解决跨域问题
- 这里使用后端支持的方式解决跨域问题
- 在 Controller 前添加注解 @CrossOrigin:默认允许所有地址跨域,也可以指定特定地址
@CrossOrigin(poigins = { "http://user.zhulang-user-center.top" }, methods = { RequestMethod.POST })
- 跨域访问成功✔
- 参数传递出现问题:传递格式有误,tagNameList 多了 [],传到后端无法识别
3. Axios 请求传参序列化
- 安装 qs 库:querystring parser 参数字符串解析器
- 再次发送请求
import {onMounted, ref} from "vue";
import myAxios from "/src/plugins/myAxios.js";
import qs from 'qs';
const { tags } = route.query;
onMounted( () => {
myAxios.get('/user/search/tags', {
params: {
tagNameList: tags,
},
paramsSerializer: params => {
return qs.stringify(params, {indices: false})
}
})
.then(function (response) {
// handle success
console.log("/user/search/tags succeed", response);
})
.catch(function (error) {
// handle error
console.log("/user/search/tags error", error);
})
.then(function () {
// always executed
});
})
- 请求参数格式正常
http://localhost:8080/api/user/search/tags?tagNameList=%E7%94%B7
- 查看后端是否接收到请求参数:接收成功并响应给前端
4. 接收后端响应数据
- 修改之前的模拟数据(之前的搜索结果页面只放了一个 mockUser 进行测试)
- 接受 GET 请求发送后,后端返回的用户列表存入 userList,在 Card 商品卡片组件中遍历展示
- await 关键字:等 myAxios 请求从后端获取到数据再存到 userListData 中
- 请求成功 .then() 响应函数:返回响应数据,从 response 对象中获取 response.data?.data
<script setup lang="ts">
import { useRoute } from'vue-router'
import {onMounted, ref} from "vue";
import myAxios from "/src/plugins/myAxios.js";
import qs from 'qs';
import type {UserType} from "@/models/user";
const route = useRoute();
const { tags } = route.query;
const userList = ref([]);
onMounted( async () => {
const userListData: UserType[] = await myAxios.get('/user/search/tags', {
params: {
tagNameList: tags,
},
paramsSerializer: params => {
return qs.stringify(params, {indices: false})
}
})
.then(function (response) {
// handle success
console.log("/user/search/tags succeed", response);
return response.data?.data;
})
.catch(function (error) {
console.log("/user/search/tags error", error);
})
if(userListData) {
userList.value = userListData;// userListData 存在则赋给 userList 进行展示
}
})
</script>
5. 处理后端响应数据格式
问题:后端保存的标签 tags 是 JSON 字符串格式,前端把每个字符都解析成了一个数组元素,即每个字符都被解析成了一个标签
- 在前端解析后端响应的 JSON 格式的字符串:将 JSON 转为数组
if(userListData) {
userListData.forEach(user => {
if(user.tags) {
user.tags = JSON.parse(user.tags);
}
});
userList.value = userListData;
}
- 查看效果
6. 搜索结果为空的页面展示
- 引入 Vant 提供的 Empty 组件
参考官方文档:Empty 空状态 - Vant 3 (gitee.io)
<van-empty image="search" description="暂无符合要求的用户" />
- 查看页面效果
附:解决跨域问题的几种方式
什么是跨域?
- 浏览器的安全问题,仅允许向同协议、同域名、同端口的服务器发送请求
- 前后端交互时有一个不同都会引起跨域
1. 方式一:修改域名和端口
- 将前后端服务的端口修改成一样的,比如都是 localhost:8080
- 通过访问路径(前缀)来转发到不同端口,比如访问 /api 就转发到 8080 端口
2. 方式二:网关支持(Nginx)——添加允许跨域配置
- proxy_pass:在配置文件中添加反向代理
- $http_origin:是 Nginx 的内置变量,设置允许所有域名跨域,但是和 * 号有点不同,* 不能和下一句的 Access-Control-Allow-Credentials 一起使用
- Access-Control-Allow-Credentials 设为 true,表示允许前端带上 cookie 请求
- 如果请求是预检请求 OPTIONS,直接返回成功 204
# 跨域配置
location ^~ /api/ {
proxy_pass http://127.0.0.1:8080/api/;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers '*';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}
3. 方式三:后端服务支持
-
SpringBoot 项目直接在接口首部加上注解 @CrossOrigin 表示允许跨域即可
-
后端支持跨域的好处
-
灵活管理多个前端的不同跨域限制:如允许域名 A 的 GET 请求跨域,域名 B 的POST 请求跨域
-
后端统一管理更加安全
-
前端不用反复写跨域配置
-