后端解决跨域问题:
在项目中新建配置类文件:
//解决跨域问题
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**") //规定哪些目录可以跨域访问
.allowedOrigins("http://localhost:5173", "http://localhost:8080") // 允许特定的源模式
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") //允许哪些方法
.allowCredentials(true); //访问时需要验证
}
}
这段代码是使用Java Spring框架中的WebMvcConfigurer接口来配置跨域资源共享(CORS)的一个例子。
CORS是一个W3C标准,它允许或限制哪些源可以访问你的资源。
在前后端分离的开发模式中,前端应用可能需要从不同的源(域名、协议或端口)请求后端服务,这时就需要配置CORS。
这段代码的配置做了以下几件事情:
- addCorsMappings(CorsRegistry registry):这个方法用于添加CORS映射,即定义哪些路径可以被跨域访问。 registry.addMapping("/**"):这行代码指定了所有路径(/**)都可以被跨域访问。
- .allowedOrigins("http://localhost:5173", "http://localhost:8080"):这行代码设置了允许跨域请求的源,即只有来自http://localhost:5173和http://localhost:8080的请求才会被接受。
- .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"):这行代码指定了允许的HTTP方法,包括GET、POST、PUT、DELETE和OPTIONS。 .allowCredentials(true):这行代码允许了凭证(如Cookies、HTTP认证等)在跨域请求中被发送。
后端默认端口号为8080,也可以在applocation.properties或者applpcation.yml文件中配置
后端controller层提供调用接口:
"/admin/login/status":是接口名称,前端只需发送http://localhost:8080/admin/login/status请求即可调用该接口
注解解释:
@PostMapping
注解:用途:用于声明一个方法处理HTTP POST请求。@GetMapping
:用于映射HTTP GET请求@PutMapping
:用于映射HTTP PUT请求,通常用于更新资源。@DeleteMapping
:用于映射HTTP DELETE请求,通常用于删除资源。@PatchMapping
:用于映射HTTP PATCH请求,用于执行部分更新操作。@RequestBody
:用于将HTTP请求体绑定到Controller方法的参数上。通常与@PostMapping
、@PutMapping
和@PatchMapping
一起使用。@ResponseBody
:用于将Controller方法的返回值作为HTTP响应体。在Spring 5之前,这个注解经常使用,但在Spring 5中,@RestController
注解可以自动应用@ResponseBody
到所有方法上,所以现在较少单独使用。@RestController
:是一个组合注解,相当于@Controller
和@ResponseBody
的组合。用于声明一个类,其所有方法的返回值都将作为HTTP响应体。
前端解决跨域问题:
前端解决跨域问题就是使用axios库来配置HTTP请求在我的这篇文章中有详细写道,里面使用的封装方法不太规范,只用于演示,大家可以根据自己需要进行更改。前端通过封装的方法就可以调用后端的接口
后端数据封装:
数据封装通常指的将接收到的数据(如前端传递的JSON对象)转换为后端可以处理的格式,比如Java对象(Java Bean)。这个过程通常涉及到以下几个步骤:
-
接收JSON数据:后端服务通过HTTP请求接收前端发送的JSON格式数据。
-
解析JSON:使用JSON解析器将JSON字符串转换为后端语言的数据结构,如在Java中通常转换为对象或Map。
-
数据封装:将解析后的数据封装成后端业务逻辑所需的格式,这可能涉及到创建对象、转换数据类型、验证数据完整性和正确性等。
-
处理业务逻辑:使用封装好的数据执行业务逻辑。
-
返回响应:将处理结果封装成JSON或其他格式,返回给前端。
比如说我要封装一个java的数据对象singer,那么就要求前端传递一个json对象
pojo层代码如下:
@TableName(value = "singer")
@Data
public class Singer {
private Integer id;
private String name;
private Byte sex;
/*头像*/
private String pic;
/*生日 。*/
// 使用 @JsonFormat 解析 JSON 中的日期格式
//用于指定如何将 Java 对象的属性序列化成 JSON 字符串,以及如何从 JSON 字符串反序列化成 Java 对象的属性。
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date birth;
/*地区*/
private String location;
/*简介*/
private String introduction;
}
controller层代码如下:
/*添加歌手*/
@PostMapping("/addSinger")
public Result addSinger(@RequestBody Singer singer){
//注解RequestBody把前端输入数据存储到params数组中
// 保存 singer 到数据库
boolean flag=singerService.insert(singer);
// 返回成功信息
if(flag){
// 保存成功
return Result.success(singer);
}else {
return Result.error("插入失败");
}
}
-
@RequestBody
:这个注解告诉 Spring MVC,参数singer
应该绑定到 HTTP 请求的 body 上。Spring 会自动将请求体中的 JSON、XML 或其他格式的数据转换成指定的对象类型。 -
Singer
:这是要绑定的数据类型,表示请求体中的数据应该符合Singer
类的属性结构。
即前端传递的json对象的data中的属性名要与Singer对象中的属性名一一对应。Singer
常包含一些属性,如姓名、年龄、地区等,这些属性与请求体中的数据结构相匹配。 -
singer
:这是方法参数的名称,它将接收转换后的Singer
对象。
返回响应:将处理结果封装成JSON或其他格式,返回给前端。Result方法如下:
//pojo层
//统一响应结果
@NoArgsConstructor //无参数的构造方法
@AllArgsConstructor //全参的构造方法
@Data
public class Result<T> {
private Integer code;//业务状态码 1-成功 0-失败
private String message;//提示信息
private T data;//响应数据
//快速返回操作成功响应结果(带响应数据)
public static <E> Result<E> success(E data){
return new Result<>(1,"操作成功",data);
}
//快速返回操作成功响应结果
public static Result success(){
return new Result(1,"操作成功",null);
}
public static Result error(String message){
return new Result(0,message,null);
}
}
前端发送的JSON格式数据:
在本片文章第二节前端解决跨域问题中我们在http文件中定义了post请求,但是我们默认的axios请求头有些问题无法直接传输json对象,后端无法接收这个请求头。
我们需要把默认请求头改为headers = {'Content-Type': 'application/json'}
-
Content-Type
:这是一个HTTP请求头字段,用于指定发送给服务器的内容的媒体类型(也称为MIME类型)。 -
application/json
:这是一个媒体类型,表示发送给服务器的内容格式为JSON。当你的请求体中包含JSON格式的数据时,应该设置这个值。这告诉服务器,你正在发送的数据是JSON格式的,服务器应该相应地解析它。
//封装post方法
export function post2json(url,data={},headers = {'Content-Type': 'application/json'}){
return new Promise((resolve,reject)=> {
axios.post(url,data,{ headers })
.then(response=>{
//成功的回调
//response是一个名字,随便起,它代表了服务器响应的所有数据,包含响应头,响应体.response.data代表的是接口响应的核心数据
resolve(response.data);
})
.catch(err=>{
//失败的回调
reject(err);
})
});
}
定义接口:
//添加歌手
export const addSinger=(params)=>post2json(`singer/addSinger`,params);
前端实现:
//双向绑定示例
<template>
<!-- 使用表单数据 -->
<form @submit.prevent="handleSubmit">
<input v-model="registerForm.name" type="text" placeholder="Name" />
<input v-model="registerForm.sex" type="text" placeholder="Sex" />
<!-- 其他表单字段 -->
<button type="submit" @click="addSingerbt">Register</button>
</form>
</template>
<script setup>
import { reactive, ref } from "vue";
import { addSinger } from "@/api";
import { ElMessage } from "element-plus";
//双向绑定
//在 Vue 3 中,reactive 是一个用于创建响应式对象的函数,这意味着当对象的属性发生变化时,Vue 会自动更新与这些属性绑定的 DOM。
let registerForm = reactive({
name: "",
sex: "",
birth: "",
location: "",
introduction: "",
});
const registerForms = ref(null);
const addSingerbt = () => {
if (registerForms.value) {//表单验证,防止空数据传输
registerForms.value.validate((valid) => {
if (valid) {
let d = registerForm.birth;
//获取当前日期对象 d 的年份、月份和日期。
//将月份加1,以得到正确的月份表示。
//将年、月、日拼接成一个字符串,格式为“年-月-日”。
const datetime = `${d.getFullYear()} - ${d.getMonth() + 1}- ${d.getDate()}`;
// 构建参数为JSON对象,参数名与后端保持一致。从双向绑定的表单中获取数据
const params = {
name: registerForm.name,
sex: Number(registerForm.sex),
pic: "/img/singerPic/txdefault.jpg", // 使用默认头像
birth: datetime,
location: registerForm.location,
introduction: registerForm.introduction,
};
// 调用 API 添加歌手
addSinger(params)
.then((res) => { //添加成功响应
if (res.code == 1) {
ElMessage({
message: "添加成功",
type: "success",
plain: true,
});
} else {
ElMessage({
message: "添加失败",
type: "error",
plain: true,
});
}
})
.catch((err) => {//添加失败响应
ElMessage({
message: "添加失败",
type: "error",
plain: true,
});
console.log(err);
});
} else {//表单验证失败
ElMessage({
message: "表单验证失败,请补全信息",
type: "error",
plain: true,
offset:90,
});
console.error("表单验证失败,请补全信息");
return false;
}
});
} else {
console.error("表单没有找到");
}
};
</script>
补充:
同样的我们还有其他的注解来获取前端传递的单一的属性值:
/*根据主键查询整个对象*/
@GetMapping("/selectByPrimarykey")
public Result selectByPrimarykey(@RequestParam("id") Integer id){
// 从前端传入的 JSON 中获取 id
if (id == null) {
return Result.error("查询失败,ID 为空");
}
// 调用服务层的查询方法
try {
// 调用服务层的查询方法
return Result.success(singerService.selectByPrimaryKey(id));
} catch (Exception e) {
return Result.error("查询失败,原因:" + e.getMessage());
}
}
-
@RequestParam
:这个注解表明后面的参数应该从HTTP请求的参数中获取值。 -
"id"
:这是请求参数的名称。在这个例子中,它告诉Spring MVC,我们希望获取名为id
的请求参数。
如果请求的URL包含像?id=123
这样的查询参数,那么id
参数的值(在这个例子中是123
)将被自动解析并绑定到方法的id
参数上。