day13_MVC_前后端分离
day13_MVC_前后端分离
1MVC
1.1MVC是一种开发思想 把数据处理与视图显示分开

注意:与后端代码三层结构是两种概念
MVC一般搭配JSP或者其他模板引擎技术使用
1.2JSP (java server page)
这个技术是为了简化servlet拼写页面而产生的
本质上还是servlet
xxxx.jsp 不能直接执行 需要经过 转换 编译才能运行
xxxx.jsp----->xxxx_jsp.java ------> xxxx_jsp.class
代码方式是java+html+css+js 代码结构比较混乱 后期维护更新很困难
使用方式 servlet+jsp
1.3跳转方式
转发
服务器内部跳转 地址栏停留在第一次请求上
req.setAttribute("msg","用户名或密码错误.....");
req.getRequestDispatcher("page/loginPage.jsp").forward(req,resp);
重定向
响应码302 带location响应头 浏览器会向location的地址重新发请求
resp.sendRedirect("page/loginPage.jsp");

2前后端分离
2.1.ajax 异步请求技术 介绍

2.2ajax代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button onclick="sendAjax()">发送ajax请求...</button>
</body>
<script>
const sendAjax = ()=>{
//异步请求对象 简称为 xhr
let xmlHttpRequest = new XMLHttpRequest();
// 处理响应的函数 回调函数
xmlHttpRequest.onreadystatechange = ()=>{
if(xmlHttpRequest.readyState == 4 &&xmlHttpRequest.status == 200){
console.log("处理数据....")
}
}
xmlHttpRequest.open("POST","/xxx")
xmlHttpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
xmlHttpRequest.setRequestHeader("myHeader","jackrose")
xmlHttpRequest.send("name=abc123&age=15")
//location.href="/xxx"
}
</script>
</html>

2.3使用axios发送ajax请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button onclick="sendAjax()">发送ajax请求...</button>
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
const sendAjax = ()=>{
//get请求
// 请求方式 地址 参数
axios.post("/day13/ajaxTest","name=jack")
.then(resp=>{
//内置对象 请求和响应状态参数
console.log(resp)
//响应体数据 直接解析json
console.log(resp.data)
})
//post请求
axios.get("/day13/ajaxTest?name=jack")
.then(resp=>{
//内置对象 请求和响应状态参数
console.log(resp)
//响应体数据 直接解析json
console.log(resp.data)
})
}
</script>
</html>
2.4后端接口标准化

接口代码格式
package com.javasm.controller;
import com.alibaba.fastjson.JSON;
import com.javasm.entity.Result;
import com.javasm.entity.ReturnCode;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @className: RegNameServlet
* @description:
* @author: gfs
* @date: 2025/8/11 14:38
* @version: 0.1
* @since: jdk17
*/
@WebServlet("/regName")
public class RegNameServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1接收请求数据
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
//2调用service
//数据库里有jack
//3根据不同的情况 做数据反馈
/*
http+json
校验用户名是否重复接口
接口文档 (说明文档)
请求地址 /day13/regName
请求参数 username string 必填项
支持请求方式 get/post
返回数据类型 json
返回数据示例
{code: 10000, msg: '用户名可用', returnData: 'green'}
{code: 10001, msg: '用户名不可用', returnData: 'red'}
* 接口反馈数据的标准化
* 1.响应头标记json格式
* resp.setContentType("application/json;charset=utf-8");
* 2.通过统一工具转化 输出json格式字符串
* 使用fastJSON转换 json格式字符串....
* 3.通过实体类Result统一返回数据的key
* @Data
//返回数据的实体类
public class Result {
//返回操作码
private Integer code;
//返回操作描述
private String msg;
//返回附加数据
private Object returnData;
}
4.通过枚举 列举出操作码和操作信息的对应关系 减少笔误
*
* */
//3.根据不同结果 返回不同的响应数据
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
Result result = new Result();
if("jack".equals(username)){
//不可用
//writer.print("{\"Msg\":\"用户名不可用\"}");
result.setCode(ReturnCode.NAME_NO_OK.getCode());
result.setMsg(ReturnCode.NAME_NO_OK.getMsg());
result.setReturnData("red");
}else{
//可用
//writer.print("{\"msg\":\"用户名可用\"}");
result.setCode(ReturnCode.NAME_OK.getCode());
result.setMsg(ReturnCode.NAME_OK.getMsg());
result.setReturnData("green");
}
writer.print(JSON.toJSONString(result));
writer.close();
}
}
接口标准化
1.响应头标记json格式
resp.setContentType("application/json;charset=utf-8");
2.通过统一工具转化 输出json格式字符串
使用fastjson工具 统一返回json格式字符串
import com.alibaba.fastjson.JSON;
import com.javasm.entity.ClassInfo;
import com.javasm.entity.Person;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* @className: JSONTest
* @description:
* @author: gfs
* @date: 2025/8/11 14:50
* @version: 0.1
* @since: jdk17
*/
public class JSONTest {
@Test
public void myJsonTest(){
//java中的数据 实体对象 集合 list map
//json格式字符串 json + 数组
/*
* Gson google的转换jar包 大而全 转换效率较低 api复杂
* jackson 外国常用转换jar包 大而全 api较简单
* fastjson alibabajar包 转换最快 api简单
*
*
* JSON.toJSONString(person) java对象 转 json字符串
* JSON.parseObject(jsonStr, Person.class) json字符串 转 指定java对象
*
* 常用的数据格式
* 1 单个对象
* 2 对象集合
* 3 对象包含集合 混合使用
* 4 map可以自定义键值对传值 尽量少用(临时性强)
* */
// Person person = new Person(1,"jack",15);
//支持任意对象 需要转换数据是 直接传给fastjson 转成json字符串
// String jsonString = JSON.toJSONString(person);
// System.out.println(jsonString);
// String jsonStr = "{\"age\":15,\"id\":1,\"name\":\"rose\"}";
// Person person = JSON.parseObject(jsonStr, Person.class);
// System.out.println(person);
// List<Person> listPerson = new ArrayList<>();
// listPerson.add(new Person(1,"jack",15));
// listPerson.add(new Person(2,"jack2",15));
// listPerson.add(new Person(3,"jack3",15));
//
// String jsonString = JSON.toJSONString(listPerson);
// System.out.println(jsonString);
// ClassInfo classInfo = new ClassInfo();
// classInfo.setClassName("张三的班");
// classInfo.setClassAddr("枫杨街");
// classInfo.setTeacher(new Person(0,"张三",55));
// List<Person> students = new ArrayList<>();
// students.add(new Person(1,"罗翔1",15));
// students.add(new Person(2,"罗翔2",15));
// students.add(new Person(3,"罗翔3",15));
// classInfo.setStudents(students);
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put("className","李四的班");
dataMap.put("teacher",new Person(0,"李四",55));
List<Person> students = new ArrayList<>();
students.add(new Person(1,"罗翔1",15));
students.add(new Person(2,"罗翔2",15));
students.add(new Person(3,"罗翔3",15));
dataMap.put("students",students);
System.out.println( JSON.toJSONString(dataMap));
}
}
3.通过实体类Result统一返回数据的key
package com.javasm.entity;
import lombok.Data;
/**
* @className: Result
* @description:
* @author: gfs
* @date: 2025/8/11 15:27
* @version: 0.1
* @since: jdk17
*/
@Data
//返回数据的实体类
public class Result {
//返回操作码
private Integer code;
//返回操作描述
private String msg;
//返回附加数据
private Object returnData;
}
4.通过枚举 列举出操作码和操作信息的对应关系
package com.javasm.entity;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* @className: ReturnCode
* @description:
* @author: gfs
* @date: 2025/8/11 15:37
* @version: 0.1
* @since: jdk17
*/
@NoArgsConstructor
@Getter
public enum ReturnCode {
NAME_OK(10000,"用户名可用"),
NAME_NO_OK(10001,"用户名不可用");
private Integer code;
private String msg;
private ReturnCode(Integer code,String msg){
this.code = code;
this.msg = msg;
}
}
2.5接口测试工具
使用独立接口测试工具 方便保存测试案例

3前端导入axios 发送ajax请求
1安装axios
pnpm add axios
2跨域请求
跨域(domain)请求
当产生了 从一个网站 访问另一个网站数据 会出现跨域请求 CORS 会通知浏览器做访问限制
默认不然产生跨域数据访问
符合CORS 在服务端 设置允许访问
浏览器对跨域的限定
http:// 127.0.0.1 : 8080
协议 地址 端口号
浏览器自动发送预检请求 method options 检测响应头 是否符合CORS要求
通过后会缓存一定事件
符合CORS策略的代码 需要在被访问端(服务器端 ) 添加响应头设置
//允许跨域访问暂时写在公共⽗类中
// /* 允许跨域的主机地址*/
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
/* 允许跨域的请求⽅法GET, POST, HEAD 等*/
resp.setHeader("Access-Control-Allow-Methods", "*");
/* 重新预检验跨域的缓存时间(s) */
resp.setHeader("Access-Control-Max-Age", "3600");
/* 允许跨域的请求头*/
resp.setHeader("Access-Control-Allow-Headers", "*");
/* 是否携带cookie */
resp.setHeader("Access-Control-Allow-Credentials", "true");
3 对aixos进行二次封装

import axios from 'axios'
//固定url前缀
axios.defaults.baseURL = 'http://localhost:8080/day13';
//post请求 自己带请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
//get post 请求统一结构 (地址,数据使用json对象)
const myPost = (url,params)=>{
return axios({
method: 'post',
url: url,
data: params
});
}
const myGet = (url,params)=>{
return axios({
method: 'post',
url: url,
params: params
});
}
export {myGet,myPost}
4前后端联调
login.vue
<template>
<el-card class="loginPanel" style="max-width: 399px">
<template #header>
<div class="card-header">
<img src="../assets/login.png"/>
</div>
</template>
<el-form ref="formRef" hide-required-asterisk :rules="rules" :model="loginForm" label-width="60" style="max-width: 380px">
<el-form-item label="用户名" prop="username">
<el-input v-model="loginForm.username" :suffix-icon="User"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" show-password v-model="loginForm.password" :suffix-icon="Lock" />
</el-form-item>
<el-form-item>
<el-button plain color="#626aef" @click="loginSubmit">登录</el-button>
<el-button plain color="#626aef" @click="loginReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Check, Delete, Edit, Message, Search, Star, Share,User,Lock } from '@element-plus/icons-vue'
import router from '@/router'
import { ElMessage,ElMessageBox } from 'element-plus'
import {myGet,myPost} from '@/myaxios'
const formRef = ref()
const loginReset = ()=>{
formRef.value.resetFields()
}
const loginSubmit = ()=>{
formRef.value.validate((valid,fields)=>{
if(valid){
console.log("提交表单数据",loginForm);
myGet("/login",loginForm)
.then(resp=>{
if(resp.data.code == 10010){
ElMessage.success(resp.data.msg)
router.push("/main")
}else if(resp.data.code == 10011){
ElMessage.error(resp.data.msg)
}
})
}
})
}
const rules = reactive({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
],
password:[
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 4, max: 8, message: '长度4-8位之间', trigger: 'blur' },
]
})
const loginForm = reactive({
username:'',
password:''
})
/*
跨域(domain)请求
当产生了 从一个网站 访问另一个网站数据 会出现跨域请求 CORS 会通知浏览器做访问限制
默认不然产生跨域数据访问
符合CORS 在服务端 设置允许访问
浏览器对跨域的限定
http:// 127.0.0.1 : 8080
协议 地址 端口号
浏览器自动发送预检请求 method options 检测响应头 是否符合CORS要求
通过后会缓存一定事件
*/
// onMounted(()=>{
// myGet("/login",{username:'aa',password:'bbb'})
// })
</script>
<style scoped>
/* scoped只对当前vue文件生效 */
.loginPanel {
margin: 300px auto;
background-color: rgba(219, 219, 219, 0.541);
}
</style>
loginServlet.java
package com.javasm.controller;
import com.alibaba.fastjson.JSON;
import com.javasm.entity.Result;
import com.javasm.entity.ReturnCode;
import com.javasm.entity.User;
import com.javasm.service.UserService;
import com.javasm.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @className: LoginServlet
* @description:
* @author: gfs
* @date: 2025/8/11 16:29
* @version: 0.1
* @since: jdk17
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//允许跨域访问暂时写在公共⽗类中
// /* 允许跨域的主机地址*/
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
/* 允许跨域的请求⽅法GET, POST, HEAD 等*/
resp.setHeader("Access-Control-Allow-Methods", "*");
/* 重新预检验跨域的缓存时间(s) */
resp.setHeader("Access-Control-Max-Age", "3600");
/* 允许跨域的请求头*/
resp.setHeader("Access-Control-Allow-Headers", "*");
/* 是否携带cookie */
resp.setHeader("Access-Control-Allow-Credentials", "true");
//1接收请求参数 转换格式 封装对象
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
//2调用service
UserService userService = new UserServiceImpl();
User loginUser = userService.login(username, password);
//3根据执行结果 返回json数据
Result result = new Result();
if(loginUser!=null){
result.setCode(ReturnCode.LOGIN_SUCCESS.getCode());
result.setMsg(ReturnCode.LOGIN_SUCCESS.getMsg());
}else{
result.setCode(ReturnCode.LOGIN_FAILED.getCode());
result.setMsg(ReturnCode.LOGIN_FAILED.getMsg());
}
//设置输出流
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print(JSON.toJSONString(result));
writer.close();
}
}
1.会写后端服务接口 注册接口 用户列表接口
2.会用post测试接口
3.写对应的页面 并前后端联调 注意跨域请求设置 和axios请求的使用

浙公网安备 33010602011771号