day13_mvc 前后端分离 - 教程
day13_mvc 前后端分离
1MVC
model (数据)模型
view 视图
control 控制
servlet+jsp 用jsp做view
如果需要数据
页面跳转
1请求转发
请求对象调用
一次请求
借助request对象 可以传任意类型
2响应重定向
响应对象调用
多次请求
只能传字符的简单数据
2前后端分离
前端 html css javascript
通过ajax请求(前端技术 发送请求的技术) 交互数据
后端 java
2.1ajax技术介绍

* ajax核心功能
* 页面不跳转的前提下 发请求与后端做数据交互
* 1.ajax通过子线程发送请求
* 2.报文结构相同 后端处理模式不变
* 3.后端会有一定的变化
* 1.后端不再传页面标签 只传处理结果
* 2.重定向功能无效
* 3.请求转发(功能削弱 很少用到)
*
*
* 前端使用vue框后 使用MVVM模式
2.2ajax原生代码
页面
Title
<script>
/*
* ajax核心功能
* 页面不跳转的前提下 发请求与后端做数据交互
* 1.ajax通过子线程发送请求
* 2.报文结构相同 后端处理模式不变
* 3.后端会有一定的变化
* 1.后端不再传页面标签 只传处理结果
* 2.重定向功能无效
* 3.请求转发(功能削弱 很少用到)
*
*
* 前端使用vue框后 使用MVVM模式
*
*
* */
const myHref = ()=>{
//异步请求对象
let xhr = new XMLHttpRequest();
//1配置回调函数
xhr.onreadystatechange = ()=>{
if(xhr.readyState == 4 && xhr.status == 200){
//请求正常 响应正常
let respData = xhr.responseText
console.log(respData)
//可以根据响应情况 写不同js处理
//......
}
}
//2.设置请求参数
xhr.open("post","/day13/ajaxDemo")
//3.设置请求头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.setRequestHeader("myHeader","jack123")
//4.发送请求
xhr.send("username=rose&age=15");
}
</script>
服务端
package com.javasm;
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: AjaxDemoServlet
* @author: gfs
* @date: 2025/10/17 10:59
* @version: 0.1
* @since: jdk17
* @description:
*/
@WebServlet("/ajaxDemo")
public class AjaxDemoServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//取请求参数
System.out.println(req.getParameter("username"));
//取请求头
String myHeader = req.getHeader("myHeader");
System.out.println(myHeader);
//通过输出流 返回数据
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print("name ok!!!");
writer.close();
}
}
2.3axios 简化ajax请求
https://www.axios-http.cn/docs/intro axios官网
简化后的ajax请求
get请求
axios.get('/day13/ajaxDemo?username=rose')
.then((resp)=>{
//请求响应数据对象
console.log(resp)
//返回的实际数据
console.log(resp.data)
})
post请求
axios.post('/day13/ajaxDemo','username=rose')
.then((resp)=>{
//请求响应数据对象
console.log(resp)
//返回的实际数据
console.log(resp.data)
})
请求格式:
//用来配置请求的其他参数
axios.post('/day13/ajaxDemo','username=rose',{headers: {'myHeader': 'zhangsan'},})
//写成功的回调
.then((resp)=>{
//请求响应数据对象
console.log(resp)
//返回的实际数据
console.log(resp.data)
})
//写失败的回调
.catch(function (error) {
// 处理错误情况
console.log(error);
})
//不管成功失败
.finally(function () {
// 总是会执行
});
原始请求格式 全用json配置
// 发起一个post请求
//全部用json做配置
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
校验用户名重复示例
页面
Title
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const checkName = ()=>{
let currentName = document.getElementById("username").value
//vue 依赖 插件
axios.post("/day13/checkName","username="+currentName)
.then(resp=>{
console.log(resp.data)
if(resp.data){
document.getElementById("checkNameSpan").innerHTML = "用户名可用"
}else{
document.getElementById("checkNameSpan").innerHTML = "用户名重复"
}
})
}
</script>
服务端
package com.javasm;
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: CheckNameServlet
* @author: gfs
* @date: 2025/10/17 11:48
* @version: 0.1
* @since: jdk17
* @description:
*/
@WebServlet("/checkName")
public class CheckNameServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String respData = "";
if("jack".equals(username)){
//重复
respData = "false";
}else{
//可用
respData = "true";
}
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print(respData);
writer.close();
}
}
3后端接口标准化
后端针对ajax请求和前后端分离 需要做一些代码格式的标准化
* 1.三层结构
* 2.servlet做控制层
* 3.数据返回json
* 可以返回多维度数据 和复杂的数据结构
* resp.setContentType("application/json;charset=utf-8")
* 4.使用统一的转换jar包
* json与java对象互相转换
* Gson google的 功能大而全 api较复杂 转换效率偏慢
* Jackson 功能大而全 转换效率偏慢 api较简单
* Fastjson 功能较全 转换块 api简单
* 验证json字符串的网站 https://www.json.cn/
* 使用fastjson (标准json格式 需要key带引号)
* JSON.toJSONString(对象) 对象 转json字符串
* User user2 = JSON.parseObject(myJsonStr, User.class) 字符串 转java对象
*
* 5.统一返回数据的key
* ReturnResult
* 操作码 code 操作信息 msg 附加数据(详细信息) returnData
*
* 6.通过枚举类 列举操作与操作信息的对应关系
* 防止程序员手误 导致操作码错乱
package com.javasm;
import com.alibaba.fastjson.JSON;
import com.javasm.entity.ReturnCode;
import com.javasm.entity.ReturnResult;
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: CheckNameServlet
* @author: gfs
* @date: 2025/10/17 11:48
* @version: 0.1
* @since: jdk17
* @description:
*/
@WebServlet("/checkName")
public class CheckNameServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
* 后端只提供数据处理服务
* (后端是远程调用的方法)
* 代码格式做标准化
*
*
*
*
* 1.三层结构
* 2.servlet做控制层
* 3.数据返回json
* 可以返回多维度数据 和复杂的数据结构
* resp.setContentType("application/json;charset=utf-8")
* 4.使用统一的转换jar包
* json与java对象互相转换
* Gson google的 功能大而全 api较复杂 转换效率偏慢
* Jackson 功能大而全 转换效率偏慢 api较简单
* Fastjson 功能较全 转换块 api简单
* 验证json字符串的网站 https://www.json.cn/
* 使用fastjson (标准json格式 需要key带引号)
* JSON.toJSONString(对象) 对象 转json字符串
* User user2 = JSON.parseObject(myJsonStr, User.class) 字符串 转java对象
*
* 5.统一返回数据的key
* ReturnResult
* 操作码 code 操作信息 msg 附加数据(详细信息) returnData
*
* 6.通过枚举类 列举操作与操作信息的对应关系
* 防止程序员手误 导致操作码错乱
*
*
* */
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
//String respData = "";
ReturnResult returnResult = new ReturnResult();
if("jack".equals(username)){
//重复
returnResult.setCode(ReturnCode.NAMECHECK_FAILED.getCode());
returnResult.setMsg(ReturnCode.NAMECHECK_FAILED.getMsg());
returnResult.setReturnData("red");
//respData = "{\"msg\":\"用户名重复\",\"color\":\"red\"}";
}else{
//可用
returnResult.setCode(ReturnCode.NAMECHECK_OK.getCode());
returnResult.setMsg(ReturnCode.NAMECHECK_OK.getMsg());
returnResult.setReturnData("green");
//respData = "{\"msg\":\"用户名可用\",\"color\":\"green\"}";
}
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print(JSON.toJSONString(returnResult));
writer.close();
}
}
接口文档:
前后端可以根据接口文档做开发 是前后端交互的重要文档
* 后端服务接口
* 校验用户名是否重复接口
* 接口文档
* 请求地址 /day13/checkName
* 请求方式 get/post
* 请求参数 username string
* 返回数据格式 json
* 返回数据示例
* 重复
* {
code: 10011,
msg: "用户名重复",
returnData: "red"
}
* 可用
{
code: 10010,
msg: "用户名可用",
returnData: "green"
}
4前端使用独立的服务器 vite
注意:
axios与vue没有集成关系 不是vue插件 是普通依赖
安装
pnpm add axios
哪个页面使用 就在那个页面引入
import axios from 'axios'
axios.get("/xxx")
跨域请求异常情况:

//跨域问题:
//从服务器A 直接访问服务器B 浏览器会做限制
//浏览器自动发送预检请求 检查请求是否出自同源 method=options
解决方法 符合cors标准
配置CORS 符合浏览器的CORS 规范
被访问端配置 允许访问的来源
/* 允许跨域的主机地址*/
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
/* 允许跨域的请求⽅法GET, POST, HEAD 等*/
resp.setHeader("Access-Control-Allow-Methods", "*");
/*重新预检验跨域的缓存时间*/
resp.setHeader("Access-Control-Max-Age", "3600");
/* 允许跨域的请求头 */
resp.setHeader("Access-Control-Allow-Headers", "*");
/* 是否携带cookie */
resp.setHeader("Access-Control-Allow-Credentials", "true");
接口例子2 省市县信息

数据库表

查询省市县信息的sql语句
select * from tb_area where parent_code = 1001
查询省市县接口
controller
package com.javasm.controller;
import com.alibaba.fastjson.JSON;
import com.javasm.entity.AreaInfo;
import com.javasm.entity.ReturnCode;
import com.javasm.entity.ReturnResult;
import com.javasm.service.AreaService;
import com.javasm.service.impl.AreaServceImpl;
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;
import java.util.List;
/**
* @className: ListAreaServlet
* @author: gfs
* @date: 2025/10/17 16:47
* @version: 0.1
* @since: jdk17
* @description:
*/
@WebServlet("/listArea")
public class ListAreaServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* 查询地区信息接口
* 接口文档
* 请求地址 /day13/listArea
* 请求方式 get/post
* 请求参数 parentCode int 必填 非必填项
* 必须要传值 有默认值
* 返回数据格式 json
* 返回数据示例
*/
/* 允许跨域的主机地址*/
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
/* 允许跨域的请求⽅法GET, POST, HEAD 等*/
resp.setHeader("Access-Control-Allow-Methods", "*");
/*重新预检验跨域的缓存时间*/
resp.setHeader("Access-Control-Max-Age", "3600");
/* 允许跨域的请求头 */
resp.setHeader("Access-Control-Allow-Headers", "*");
/* 是否携带cookie */
resp.setHeader("Access-Control-Allow-Credentials", "true");
//1接收请求参数 转换格式 封装对象
String parentCodeStr = req.getParameter("parentCode");
Integer parentCode = 0;
//没有key null 有key 没值 ""
if(parentCodeStr!=null&&!"".equals(parentCodeStr)){
parentCode = Integer.parseInt(parentCodeStr);
}
//2调用service
AreaService areaServce = new AreaServceImpl();
List areaInfos = areaServce.listAreaByParentCode(parentCode);
//3根据执行结果 返回json数据
ReturnResult returnResult = new ReturnResult(ReturnCode.QUERY_SUCCESS.getCode(),
ReturnCode.QUERY_SUCCESS.getMsg(),
areaInfos);
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.print(JSON.toJSONString(returnResult));
writer.close();
}
}
service
package com.javasm.service.impl;
import com.javasm.dao.impl.AreaDaoImpl;
import com.javasm.entity.AreaInfo;
import com.javasm.service.AreaService;
import java.util.List;
/**
* @className: AreaServceImpl
* @author: gfs
* @date: 2025/10/17 16:58
* @version: 0.1
* @since: jdk17
* @description:
*/
public class AreaServceImpl implements AreaService {
@Override
public List listAreaByParentCode(Integer parentCode) {
return new AreaDaoImpl().listAreaByParentCode(parentCode);
}
}
dao
package com.javasm.dao.impl;
import com.javasm.dao.AreaDao;
import com.javasm.entity.AreaInfo;
import com.javasm.entity.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @className: AreaDaoImpl
* @author: gfs
* @date: 2025/10/17 16:52
* @version: 0.1
* @since: jdk17
* @description:
*/
public class AreaDaoImpl implements AreaDao {
@Override
public List listAreaByParentCode(Integer parentCode) {
//jdbc 流程比较繁琐
List listArea = new ArrayList<>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1 创建连接
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//创建连接
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb", "root", "root");
//2准备请求数据请求
String sql = "select * from tb_area where parent_code = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,parentCode);
//3发送请求 接收反馈
resultSet = preparedStatement.executeQuery();
//4读取响应报文中的数据内容 做成java中的对象数据
while(resultSet.next()){
int areaCode = resultSet.getInt("area_code");
String areaName = resultSet.getString("area_name");
String parentCodeDB = resultSet.getString("parent_code");
listArea.add(new AreaInfo(areaCode,areaName,parentCode)) ;
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
try{
if(resultSet!=null)resultSet.close();
if(preparedStatement!=null)preparedStatement.close();
if(connection!=null)connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return listArea;
}
}
entity
package com.javasm.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @className: AreaInfo
* @author: gfs
* @date: 2025/10/17 16:50
* @version: 0.1
* @since: jdk17
* @description:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AreaInfo {
private Integer areaCode;
private String areaName;
private Integer parentCode;
}
写完接口后 使用接口测试工具 测试接口的使用


浙公网安备 33010602011771号