当前位置: 首页 > news >正文

Vue和Springboot初步前后端分离建立项目连接(解决前后端跨域问题)

前后端分离

前端端口号配置5000
后端端口号配置8080

1.vue前端初始化

在主页本专栏上篇《Vue复习》的初始化Vue基础上,加入向后端发送get请求的代码,此处以Customer.Vue为例。
在这里插入图片描述

<template><h1>客户管理界面</h1>
</template><style></style><script setup>
import {onMounted} from  "vue"  //引入钩子
import axios  from "axios";
import api from "@/util/api.js";
//当组件挂载完毕之后触发
onMounted(async ()=>{// await阻塞等待 必须用在async函数中let resp = await axios( {  //await 确保了axios()后的 console.log(resp) 只有在 API 请求完成并获得响应后才会执行,这是处理异步数据获取的标准模式。url:"http://localhost:8080/api/v1/members",method:"get",params:{page:1,limit:10,name: "晓"}});console.log(resp);
});</script>

2.初始化springboot整合ssm的后端程序

2.1创建项目引入依赖

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.5</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
<!--        mybatis-plus--><!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-spring-boot3-starter --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.12</version></dependency><!--        mybatis-plus的自动分页插件--><!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-jsqlparser --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser</artifactId><version>3.5.12</version></dependency></dependencies>

2.2写项目初始化配置文件

spring:application:name: crm2026#配置数据源datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/gcbs2025?characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueusername: rootpassword: 123456
# 配置mybatis
mybatis-plus:configuration:log-prefix: mybatis.log-impl: org.apache.ibatis.logging.commons.JakartaCommonsLoggingImplmap-underscore-to-camel-case: truelazy-loading-enabled: truecache-enabled: falsetype-aliases-package: com.ysy.crm2026.modelmapper-locations: classpath*:/mapper/**/*.xml
server:port: 8080
logging:level:mybatis: debug

2.3完成查询会员信息的基础Mvc框架搭建

项目结构:
在这里插入图片描述

2.3.1model层

@TableName(“t_member”)和 @TableField(condition = SqlCondition.LIKE)等都是mabatisplus的注解

/*
* 会员
* */
@TableName("t_member") //指明该类对应哪个数据库的表,方便查询
@Getter
@Setter
public class Member extends AuditEntity {@TableId(type = IdType.AUTO)//指定主键类型private  Integer id;@TableField(condition = SqlCondition.LIKE)//表示用like拼接,不写默认用=拼接
//    @TableField("mobile")  phone对应数据库表里的mobile字段private String  phone;private String password;@TableField(condition = SqlCondition.LIKE)private String name;private String pinyin;private String sex;private LocalDate birthday;@TableField(condition = SqlCondition.LIKE)private String  email;@TableField(condition = SqlCondition.LIKE)private String  wechat;private String  description;
}

import java.time.LocalDate;
@Getter
@Setter
public class MemberSearchBean extends Member {//以查询出生日期范围举例,假设有这个需求private LocalDate birthdayFrom;private LocalDate birthdayTo;
}
/*
* 审计字段
* *///因为可能每个表都有审计字段,所以把这个字段的类单独定义出来,又因为他不属于业务,不能单独创建对象,所以定义为抽象类
public abstract class AuditEntity {private String createdBy;private LocalDate createdTime;private  String updatedBy;private LocalDate updatedTime;
}
2.3.2DAO(Mapper)层
@Mapper
//BaseMapper也是Mybaitsplus的,有一些便捷的增删改查操作接口方便
public interface MemberMapper extends BaseMapper<Member> {//  Page类是Mybatisplus自带的,可以用于分页default Page<Member> findAll(Page<Member> page, MemberSearchBean msb) {LambdaQueryWrapper<Member> qw = Wrappers.lambdaQuery(msb);//LambdaQueryWrapper  一个查询器if(msb.getBirthdayFrom()!=null){qw.ge(Member::getBirthday,msb.getBirthdayFrom());//qw封装一个大于等于msb.getBirthdayFrom()的Member里的birthday字段}if(msb.getBirthdayTo()!=null){qw.lt(Member::getBirthday,msb.getBirthdayTo());//qw封装一个小于等于msb.getBirthdayTo()的Member里的birthday字段}return selectPage(page,qw);//selectPage也是BaseMapper里的,返回分页查询的内容}}
2.3.3Service层

/***  查询全部会员* @param page 分页对象* @param msb  查询条件* @return  结果* */public interface MemberService {Page<Member> findAll(Page<Member> page, MemberSearchBean msb);
}
@Service
public class MemberServiceImpl implements MemberService {private MemberMapper memberMapper;@Autowiredpublic void setMemberMapper(MemberMapper memberMapper) {this.memberMapper = memberMapper;}@Overridepublic Page<Member> findAll(Page<Member> page, MemberSearchBean msb) {return memberMapper.findAll(page,msb);}
}
2.3.4Api层

此处为了封装响应对象,定义了一个名为JsonResult的封装类:

@Getter
@Setter
@AllArgsConstructor
public class JsonResult<T> {private int code;private boolean success;private String msg;private T data;public static JsonResult<?> success() {return success(null);}public static <T> JsonResult<T> success(T data) {return new JsonResult<>(200, true, "操作成功", data);}public static JsonResult<?> fail(int code, String msg) {return new JsonResult<>(code, false, msg, null);}public static JsonResult<?> fail(String msg) {return fail(500, msg);}
}

MemberApi类:

@RestController
@RequestMapping(value = "/api/v1/members", produces = MediaType.APPLICATION_JSON_VALUE)
public class MemberApi {private MemberService memberService;@Autowiredpublic void setMemberService(MemberService memberService) {this.memberService = memberService;}@GetMappingpublic ResponseEntity<JsonResult<?>> findAll(@RequestParam(defaultValue = "1") Integer pageNo,//默认值为1@RequestParam(defaultValue = "20") Integer pageSize,MemberSearchBean msb) {//分页对象Page<Member> page = new Page<>(pageNo,pageSize);//查询全部会员page = this.memberService.findAll(page,msb);return  ResponseEntity.ok(JsonResult.success(page));}}

2.4引入mybatisplus的自动分页拦截器

@Configuration
public class CommonConfig {//mybatis-plus自动分页拦截器@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}

目前进行到这,一个接收"http://localhost:8080/api/v1/members"的get请求的后端框架以及一个向后端发送get请求的前端框架已经搭建完毕
但此时,启动启动器,前端得不到后端返回的数据,这是因为,浏览器默认的遵守同源协议,也就是一个域只能访问他自己域里的内容,不能跨域访问,从前端5000端口号向后端8080端口号发送请求接受请求,造成了跨域,因此数据不能正常传输,会报错。

2.5解决跨域问题

一开始,前端的Customer.vue中有这段请求代码:

 let resp = await axios( {  //await 确保了axios()后的 console.log(resp) 只有在 API 请求完成并获得响应后才会执行,这是处理异步数据获取的标准模式。url:"http://localhost:8080/api/v1/members",method:"get",params:{page:1,limit:10,name: "晓"}});

注意这的 url:"http://localhost:8080/api/v1/members",指的是前端向这个地址发送请求,但我们在vue的配置文件里定义的是5000端口,遵循上面提到的同源策略,在这发送这个url请求是发不过去的,因此:

2.5.1首先在vite.config.js里加入以下配置

在这里插入图片描述

  //代理proxy:{"/api":{ //只要前端地址有/api,就给代理到target的请求target:"http://localhost:8080",changeOrigin:true,rewrite:url=>url.replace(/^\/api/,"/api/v1")//匹配以/api开头的字符串.替换到/api/v1}}

这段代码指的是,给上述vue前端添加一个代理,但前端请求的url路径中,发现有"/api",就将这个"/api"匹配到下面定义target:“http://localhost:8080”,后面。并且,将"/api",替换成"/api/v1"
此时Customer.vue中有这段请求代码的url进行修改:

 let resp = await axios( {  //await 确保了axios()后的 console.log(resp) 只有在 API 请求完成并获得响应后才会执行,这是处理异步数据获取的标准模式。url:"api/members",method:"get",params:{page:1,limit:10,name: "晓"}});

url:“api/members”,其实这个全称是:http://localhost:5000/api/members,此时相当于是Custome.vue向`http://localhost:5000/api/members路径发送请求,端口号是5000,可以绕过同源策略不触发,然后在这个请求发送的过程中,vite.config.js中的代理:

//代理proxy:{"/api":{ //只要前端地址有/api,就给代理到target的请求target:"http://localhost:8080",changeOrigin:true,rewrite:url=>url.replace(/^\/api/,"/api/v1")//匹配以/api开头的字符串.替换到/api/v1}}

这段代码发挥作用,将http://localhost:5000/api/members拦截,识别到了“/api”,此时,将http://localhost:5000替换成了http://localhost:8080,然后执行
rewrite:url=>url.replace(/^\/api/,"/api/v1"),把“/api”替换成了"/api/v1"。
此时,经过这个代理拦截后的一系列操作,请求的url已经变成了http://localhost:8080/api/v1//members,也就是后端要接收的正确路径,这样,其实就可以进行前后端交互了。

因为还在项目中定义了一个util包,里面有个api.js,代码如下

import axios from "axios";
//创建一个axios实例
let api = axios.create({baseURL:"/api",timeout:3000
});
//配置响应拦截器
api.interceptors.response.use(resp=>{return resp.data;
},resp=>{return Promise.reject(resp.data.msg||"后台服务异常");
});
export default api;

先不用看配置响应拦截器,只看创建一个axios实例,里面定义了 baseURL:“/api”,意思是所有通过该实例的请求会自动拼接 /api 前缀,也就是说,咱们的Customer.vue里,url还可以简写:
首先引入import api from "@/util/api.js";
然后url改成 url:"/members"
完整代码如下:

<template><h1>客户管理界面</h1>
</template><style></style><script setup>
import {onMounted} from  "vue"  //引入钩子
import axios  from "axios";
import api from "@/util/api.js";
//当组件挂载完毕之后触发
onMounted(async ()=>{// await阻塞等待 必须用在async函数中let resp = await axios( {  //await 确保了axios()后的 console.log(resp) 只有在 API 请求完成并获得响应后才会执行,这是处理异步数据获取的标准模式。// url:"http://localhost:8080/api/v1/members",url:"/members",//vite.config.js跨域配置和util的api.js配置后这样写method:"get",params:{page:1,limit:10,name: "晓"}});console.log(resp);
});</script>
http://www.dtcms.com/a/323068.html

相关文章:

  • linux安装php
  • 机器学习 K-Means聚类 无监督学习
  • AI 算法优化实战指南:从理论到部署的全流程优化策略
  • VSCode添加Python、Java注释技巧、模板
  • 企业级web应用服务器TOMCAT入门详解
  • 2G内存的服务器用宝塔安装php的fileinfo拓展时总是卡死无法安装成功的解决办法
  • Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2) C、D、E
  • 数码管的使用(STC8)
  • 美股高频分时Tick数据分钟级解析
  • Leetcode-19. 删除链表的倒数第 N 个结点
  • 机器学习第七课之支持向量机SVM
  • 【线性代数】线性方程组与矩阵——(3)线性方程组解的结构
  • 如何在 Windows 下使用 WSL 安装 Ubuntu 并配置国内镜像
  • 力扣前200题字符串总结
  • 差分放大电路分析与仿真
  • 阿里Qwen-Image本地部署详细指南
  • 机器翻译正则化技术详解:防止过拟合的有效方法
  • 推客系统开发全攻略:从架构设计到高并发实战
  • 【Python 高频 API 速学 ⑤】
  • 软考 系统架构设计师系列知识点之杂项集萃(120)
  • 使用jlink-gdb-server 加 gdb调试嵌软2
  • 2025年SEVC SCI2区,基于深度强化学习与模拟退火的多无人机侦察任务规划,深度解析+性能实测
  • 压力传感器选型铁三角:介质·安全·精度
  • 多模型动态路由框架实践:提升推理效率与资源利用率的技术方案
  • 数据结构5.(哈希表及数据的排序和查找算法)
  • GPT-5的4个缺点
  • 数据结构初阶(7)树 二叉树
  • 使用qemu运行与GDB调试内核
  • 解决python错误:playwright._impl._errors.TimeoutError: Timeout 30000ms exceeded.
  • STM32——时钟系统