Filter过滤器
概述
Filter 即过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。其核心作用是在客户端对服务器资源发起请求时进行拦截,进而实现特定功能。
工作原理
如下图所示,浏览器可以访问服务器上的所有的资源(Servlet、JSP、HTML 等)\
请求可以先经过 过滤器Filter,Filter 可对请求进行处理,之后再决定是否将请求放行至目标资源。如下图\
核心作用
通用性重复操作写在过滤器中,避免在每个资源中编写相同代码(如权限校验、编码处理),提升可维护性。(如 权限控制、统一编码处理、敏感字符过滤、日志记录等)
权限控制漏洞
之前 JavaWeb(3-会话技术) 中做的品牌数据管理的案例中的 登录 的功能 存在一个未授权访问漏洞 。即使不登录,在浏览器直接访问品牌数据页面(http://localhost:8080/项目名/selectAllServlet
) ,可以看到 查询所有
的超链接\
直接点击该按钮,即使用户未登录也能看到品牌的数据,这是一个安全漏洞。
修补漏洞需要 服务端对selectAllServlet
(品牌数据页面)进行访问权限验证,需要在每一个资源中都写上这段逻辑,而像这种通用的操作,我们就可以放在过滤器中进行实现。这个就是 权限控制 。
快速入门
开发步骤
进行 Filter
开发分成以下三步实现
- 定义类,实现 Filter接口,并重写其所有方法\
- 配置Filter拦截资源的路径:在类上定义
@WebFilter
注解。而注解的value
属性值/*
表示拦截所有的资源\ - 在doFilter方法中输出一句话,并放行\
上述代码中的
chain.doFilter(request,response);
就是放行,也就是让其访问本该访问的资源。
代码演示
创建一个项目,项目下有一个 hello.jsp
页面,项目结构如下:\
pom.xml
配置文件内容如下:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>filter-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
</configuration>
</plugin>
</plugins>
</build>
</project>
hello.jsp
页面内容如下:
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>hello JSP~</h1>
</body>
</html>
我们现在在浏览器输入 http://localhost/filter-demo/hello.jsp
直接访问 hello.jsp
页面,这里是可以访问到 hello.jsp
页面内容的。\
接下来编写过滤器。过滤器是 Web 三大组件之一,所以我们将 filter
创建在 com.itheima.web.filter
包下,起名为 FilterDemo
java
@WebFilter("/*") // 关键配置:定义拦截路径
public class FilterDemo implements Filter {
// 核心处理方法
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo..."); //拦截逻辑执行位置
}
// 初始化方法(可选)
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
// 销毁方法(可选)
@Override
public void destroy() {
}
}
重启启动服务器,再次重新访问 hello.jsp
页面,这次发现页面没有任何效果,但是在 idea
的控制台可以看到如下内容:
上述显示说明 FilterDemo
这个过滤器的 doFilter()
方法执行了,但是在浏览器上看不到 hello.jsp
页面的内容,因为此时hello.jsp
页面请求被拦截在过滤器阶段 而未放行。在 doFilter()
方法中添加放行的方法才能访问到 hello.jsp
页面:
java
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("1.FilterDemo...");
//放行
chain.doFilter(request,response); }
再次重启服务器并访问 hello.jsp
页面,发现这次就可以在浏览器上看到页面效果。
执行流程
使用过滤器的流程 \
流程详解
- 请求进入过滤器链:
当客户端请求到达时,首先进入第一个过滤器,执行过滤器的doFilter()
方法中的 放行前逻辑 (如权限验证、日志记录等)。 - 放行请求:
调用chain.doFilter(request, response)
方法,请求被传递到 下一个过滤器(如果存在多个过滤器)或 最终到达目标资源(Servlet/JSP/静态资源)。 - 资源处理阶段:
目标资源执行自己的业务逻辑,生成响应内容(如 HTML/JSON 数据)。 - 返回过滤器链:
资源处理完成后,控制权 沿原路径返回 过滤器链,执行chain.doFilter()
之后的 放行后逻辑 ;多个过滤器时,执行顺序与放行顺序 相反(原路返回)。
测试验证
接下来我们通过代码验证一下,在 doFilter()
方法前后都加上输出语句,如下:\
同时在 hello.jsp
页面加上输出语句,如下:\
启动服务器访问 hello.jsp
页面,在控制台打印的内容如下:
可以将对请求进行处理的代码放在放行之前进行处理,而如果请求完资源后还要对响应的数据进行处理时可以在放行后进行逻辑处理。
拦截路径配置
核心注解 :@WebFilter("拦截路径")
匹配规则 :Filter 配置路径匹配的是 资源路径部分 (不包括协议/主机/上下文路径/查询参数)
请求 URI 结构示例:
text
协议 主机 上下文路径 资源路径 查询参数
┌──┐ ┌───┐ ┌──────┐┌──────────────┐ ┌─────────┐
http://domain.com/webapp/user/profile.jsp?param=value
配置方式(四种):
精确资源拦截
拦截对象:任何可通过 URL 直接访问的资源
可拦截资源类型 | 配置示例 | 实际拦截路径 |
---|---|---|
JSP 页面 | /login.jsp |
http://domain/app/login.jsp |
HTML 静态文件 | /help.html |
http://domain/app/help.html |
Servlet 路径 | /UserServlet |
http://domain/app/UserServlet |
图片资源 | /images/logo.png |
http://domain/app/images/logo.png |
CSS/JS 文件 | /css/style.css |
http://domain/app/css/style.css |
注意:配置路径区分大小写;忽略查询参数(请求路径:/index.jsp?lang=en
,配置路径/index.jsp
)
目录拦截
拦截对象:指定目录下的所有资源
配置示例:/user/add
, /user/profile
注意:不拦截 /目录名
本身
后缀名拦截
拦截所有最后一个后缀名为指定后缀名的资源
配置示例:*.jsp
、*.html
、*.css
全局拦截
拦截对象:拦截所有请求(包括静态资源、Servlet、JSP等
配置示例:/*
注意:通常需要排除登录页/静态资源,避免死循环
尽管配置语法相似,但 Filter 路径 和 Servlet 路径 无关。
过滤器链
概述:过滤器链是指在一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。
上图中的过滤器链执行是按照以下流程执行:
- 执行
Filter1
的放行前逻辑代码 - 执行
Filter1
的放行代码 - 执行
Filter2
的放行前逻辑代码 - 执行
Filter2
的放行代码 - 访问到资源
- 执行
Filter2
的放行后逻辑代码 - 执行
Filter1
的放行后逻辑代码
多个 Filter 的执行顺序
配置方式 | 执行顺序规则 | 控制方法 |
---|---|---|
@WebFilter 注解 | 按 类名字母顺序 执行(A-Z) 例:AFilter → LFilter → ZFilter |
调整类名前缀 |
web.xml 配置 | 按 <filter-mapping> 声明顺序执行 |
调整 web.xml 中的配置顺序 |
混合配置时,@WebFilter
注解 优先于 完全匹配 Web 应用根目录下的具体资源路径 web.xml
配置,同类配置按字母顺序。
测试验证
-
编写第一个过滤器
FilterDemo
,配置成拦截所有资源java//1. 放行前,对 request数据进行处理 System.out.println("1.FilterDemo..."); //放行 chain.doFilter(request,response); //2. 放行后,对Response 数据进行处理 System.out.println("3.FilterDemo...");
-
编写第二个过滤器
FilterDemo2
,配置成拦截所有资源java//1. 放行前,对 request数据进行处理 System.out.println("2.FilterDemo..."); //放行 chain.doFilter(request,response); //2. 放行后,对Response 数据进行处理 System.out.println("4.FilterDemo...");
-
修改
hello.jsp
页面中脚本的输出语句jsp<% System.out.println("3.hello jsp"); %>
-
启动服务器,在浏览器输入
http://localhost/filter-demo/hello.jsp
进行测试,在控制台打印内容如下
案例
需求
访问服务器资源时,需要先进行登录验证,如果没有登录,则自动跳转到登录页面
分析
无需在每一个资源里加入登录状态校验的代码,只需要写一个 Filter
,在该过滤器中进行登录状态校验即可。而在该 Filter
中逻辑如下:
代码实现
创建Filter
在 brand-demo
工程创建 com.itheima.web.filter
包,在该下创建名为 LoginFilter
的过滤器
java
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
实现逻辑
在 doFilter()
方法中编写登陆状态校验的逻辑代码。
-
从
session
对象中获取用户信息,但是ServletRequest
类型的 requset 对象没有获取 session 对象的方法,因此需要将 request对象强转成HttpServletRequest
对象。javaHttpServletRequest req = (HttpServletRequest) request;
-
获取Session对象
-
从Session对象中获取名为
user
的数据 -
判断获取到的数据是否是 null
- 如果不是,说明已经登陆,放行
- 如果是,说明尚未登陆,将提示信息存储到域对象中并跳转到登陆页面
放行静态资源
问题现象 :
当用户未登录时,过滤器会拦截所有请求并重定向到登录页面,包括对CSS、图片等静态资源的请求。这导致登录页面无法正确加载样式,显示为无样式的原始 HTML 页面。
解决思路 :
在过滤器中,对于特定的资源(如登录页面、注册页面、CSS文件、图片、验证码生成Servlet等)应该直接放行,而不进行登录状态的检查。这样,当用户访问这些资源时,即使未登录也能正常获取资源。
具体做法:
- 创建一个放行路径列表(数组),包含需要放行的资源路径(如:登录页面路径、注册页面路径、CSS目录、图片目录、登录处理的Servlet路径、注册处理的Servlet路径、验证码Servlet路径等)。
- 在过滤器的
doFilter
方法中,首先获取当前请求的URL。 - 检查当前请求的URL是否包含在放行路径列表中。如果包含,则直接调用
chain.doFilter()
放行,不再执行后续的登录状态检查。 - 如果当前请求的URL不在放行路径列表中,则进行登录状态检查:检查session中是否存在用户信息(如"user"属性)。
- 如果存在(已登录),则放行。
- 果不存在(未登录),则跳转到登录页面,并提示用户登录。
综上,我们需要在判断session中是否包含用户信息之前,应该加上对登录及注册相关资源放行的逻辑处理。
过滤器完整代码
java
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
// 1. 将通用ServletRequest转换为HttpServletRequest以访问Session
HttpServletRequest req = (HttpServletRequest) request;
//2. 判断访问资源路径是否和登录注册相关
//在数组中存储登陆和注册相关的资源路径
String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};
//获取当前访问的资源路径
String url = req.getRequestURL().toString();
//遍历数组,获取到每一个需要放行的资源路径
for (String u : urls) {
//判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串
if(url.contains(u)){
chain.doFilter(request, response); //找到了,放行
return;
}
}
// 3. 获取当前请求的Session对象(如果不存在则创建新Session)
HttpSession session = req.getSession();
// 4. 从Session中获取用户登录凭证(通常存储为"user"属性)
Object user = session.getAttribute("user");
// 5. 用户登录状态验证
if(user != null){ // 用户已登录
// 放行请求,允许访问目标资源
chain.doFilter(request, response);
}else { // 用户未登录
// 在请求作用域存储登录提示信息(login.jsp页面可读取)
req.setAttribute("login_msg","您尚未登陆!");
// 转发到登录页面(保持请求响应对象)
req.getRequestDispatcher("/login.jsp").forward(req,response);
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
注意:上述代码使用 contains 方法判断资源路径是否匹配可能会产生误判。这里暂且使用。
Listener监听器
概述
-
Listener 表示监听器,是 JavaWeb 三大核心组件之一 (Servlet、Filter、Listener)。
-
监听器就是在
application
,session
,request
三个对象的- 对象创建、对象销毁;
- 添加(Add)、修改(Replace)、删除(Remove) 属性;
- 钝化(Passivation) 和 活化(Activation) (对于会话
HttpSession
) 时
自动执行预先定义的代码的功能组件。
application
是ServletContext
类型的对象。
ServletContext
代表整个web应用,在服务器启动的时候,tomcat会自动创建该对象。在服务器关闭时会自动销毁该对象。
分类
JavaWeb 提供了8个监听器:
核心监听器 ServletContextListener
:用于监听 ServletContext
对象(即 application
对象)的生命周期事件。
接口方法:
void contextInitialized(ServletContextEvent sce)
: 当ServletContext
被创建后立即执行 用于加载全局资源(如数据库连接池、配置文件)void contextDestroyed(ServletContextEvent sce)
:当ServletContext
被销毁前执行 用于释放全局资源(如关闭连接、清理临时文件)
代码演示
演示一下 ServletContextListener
监听器
- 定义一个类,实现
ServletContextListener
接口 - 重写所有的抽象方法
- 使用
@WebListener
进行配置
代码如下:
java
@WebListener // 声明为Web监听器组件,无需XML配置
// 实现ServletContext监听接口
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) { // 应用启动时自动执行的回调方法
// 资源初始化操作(如加载配置/连接池等)
System.out.println("ContextLoaderListener初始化执行..."); // 服务器启动日志可见此输出
}
@Override
public void contextDestroyed(ServletContextEvent sce) { // 应用停止时自动执行的回调方法
// 资源清理操作(如关闭连接/释放资源等)
}
}
启动服务器,可以在启动的日志信息中看到 contextInitialized()
方法输出的内容,说明 ServletContext
对象在服务器启动的时候被创建了。
Ajax
概述
AJAX (Asynchronous JavaScript And XML):异步的 JavaScript 和 XML。 核心组成:
JavaScript
:实现前端交互逻辑XML
:数据交换格式(现多用 JSON)
异步特性:通信过程不阻塞页面交互
核心作用
- 与服务器数据交换
- AJAX 通过 JavaScript 直接发送 HTTP 请求到服务器
- 服务器处理后将数据直接返回浏览器(非完整页面)
- 前端使用 HTML/CSS 动态展示数据,替代传统 JSP 页面渲染模式
- 异步交互体验
- 在不重新加载整个页面的情况下,与服务器交换数据并仅更新网页部分区域
- 即时响应:实时获取/提交数据不中断用户操作
- 如:搜索联想、用户名是否可用校验,等等...\
当用户输入一些关键字(如奥运
)后就会在下面联想出 存储在百度的服务器上 的相关的内容,但不需要页面重新刷新,这就是更新局部页面 的效果。\
在用户名的输入框输入用户名,当输入框一失去焦点,如果用户名已经被占用就会在下方展示提示的信息。
AJAX 模式 相较 传统JSP模式 的优越性
之前使用传统JSP模式做功能的流程,如下图:
如上图,Servlet
调用完业务逻辑层后将数据存储到域对象中,然后跳转到指定的 jsp
页面,在页面上使用 EL表达式
和 JSTL
标签库进行数据的展示。
而使用 AJAX 模式 后,就可以使用AJAX和服务器进行通信,以达到使用 HTML+AJAX来替换JSP页面了。如下图,浏览器发送请求servlet,servlet 调用完业务逻辑层后将数据直接响应回给浏览器页面,页面使用 HTML 来进行数据展示。
同步和异步
同步发送请求过程如下
- 流程特点:浏览器发送请求 → 服务器处理(浏览器完全等待)→ 服务器响应 → 浏览器继续操作
- 用户感知:请求发出后页面完全冻结(无法滚动/点击/输入),必须等待服务器响应完成才能恢复操作
- 技术表现:页面会出现加载旋转图标或空白等待状态,典型的整页刷新行为(如传统表单提交)
异步发送请求过程如下
- 流程特点:浏览器发送请求 → 继续正常操作页面(后台等待响应)→ 收到响应后局部更新
- 用户感知:请求发出后页面保持可操作状态,无感知等待,响应后仅更新指定区域
- 技术表现:无页面刷新(地址栏不变),典型的局部更新(如搜索联想、实时验证)
快速入门
服务端实现
- 在项目的
src/main/java
目录下,创建包结构com.itheima.web.servlet
,并在该包下创建名为AjaxServlet
的 servlet
java
@WebServlet("/ajaxServlet") // 定义AJAX请求的访问路径
public class AjaxServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
// 直接返回字符串响应(实际开发中常返回JSON)
response.getWriter().write("hello ajax~");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
客户端实现
在 webapp
下创建名为 01-ajax-demo1.html
的页面,在该页面书写 ajax
代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 1. 创建 XMLHttpRequest 对象(兼容不同浏览器)
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest(); // 现代浏览器(Chrome/Firefox/Edge等)
} else {
xhttp = new ActiveXObject("Microsoft.XMLHTTP"); // 兼容 IE5/IE6
}
// 2. 设置状态变化回调函数以获取响应
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) { // 当请求完成(即readyState=4)且响应成功(即status=200)
alert(this.responseText); // 弹出显示通过 this.responseText 获取到服务端响应的数据
}
};
//3. 配置异步请求以建立连接
// 参数说明:请求方法, 目标URL, 是否异步(默认true)
xhttp.open("GET", "http://localhost:8080/ajax-demo/ajaxServlet");
//4. 发送请求到服务器
xhttp.send();
</script>
</body>
</html>
测试
在浏览器地址栏输入 http://localhost:8080/ajax-demo/01-ajax-demo1.html
,在 01-ajax-demo1.html
加载的时候就会发送 ajax
请求,效果如下
我们可以通过 开发者模式
查看发送的 AJAX 请求。在浏览器上按 F12
快捷键
这个是查看所有的请求,如果我们只是想看 异步请求的话,点击上图中 All
旁边的 XHR
,会发现只展示 Type 是 xhr
的请求。如下图:
案例
需求
在进行用户注册时,当用户名输入框失去焦点,校验用户名是否在数据库已存在\
分析
前端实现逻辑
- 事件绑定:为用户名输入框绑定
onblur
事件(失去焦点触发) - 请求发送:获取输入框中的用户名,通过 AJAX 发送请求到后端校验接口;请求携带参数:
username=输入值
- 响应处理:解析后端返回的校验结果,根据结果动态更新页面提示信息:
- 用户名已存在 → 显示红色错误提示
- 用户名可用 → 显示绿色通过提示
后端实现逻辑
- 请求处理:接收前端传递的
username
参数 - 业务校验:调用 Service 层查询数据库,检查用户名是否存在(此案例仅演示流程,不实际查询)
- 响应返回:返回简单标记数据
整体流程如下:
后端实现
在 com.ithiema.web.servlet
包中定义名为 SelectUserServlet
的servlet。代码如下:
java
@WebServlet("/selectUserServlet") // 定义用户名校验接口路径
public class SelectUserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取前端传递的用户名参数
// 实际项目还应进行参数校验(如非空/长度等)
String username = request.getParameter("username");
// 2. 模拟业务逻辑处理(实际项目需调用Service层查询数据库)
boolean flag = true; // 直接返回true表示用户名已存在(仅用于演示)
// 3. 将校验结果转换为字符串响应给前端、
// 实际项目应返回JSON格式数据:{"exists":true}
response.getWriter().write("" + flag);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
前端实现
将 04-资料\1. 验证用户名案例\1. 静态页面
下的文件整体拷贝到项目下 webapp
下。并在 register.html
页面的 body
结束标签前编写 script
标签,在该标签中实现如下逻辑:
- 给用户名输入框绑定光标失去焦点事件
- 发送 ajax请求,携带username参数
- 处理服务端响应,判断是否显示提示信息
综上所述,前端完成代码如下:
js
//1. 给用户名输入框绑定 失去焦点事件
document.getElementById("username").onblur = function() {
//2. 发送ajax请求
//2.1 获取当前用户名输入框的值
var username = this.value; // //this:给谁绑定的事件,this就代表谁。这里this指向当前输入框DOM元素
//2.2 创建XHR对象(兼容浏览器)
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2.3 设置状态变化回调函数以获取响应
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// 3. 处理服务端响应
if(this.responseText == "true") {
// 用户名已存在:显示错误提示
document.getElementById("username_err").style.display = '';
} else {
// 用户名可用:隐藏错误提示
document.getElementById("username_err").style.display = 'none';
}
}
};
// 4. 配置并发送GET请求(携带用户名参数)
xhttp.open("GET", "http://localhost:8080/ajax-demo/selectUserServlet?username=" + username);
xhttp.send(); // 发送请求到服务器
};
Axios
Axios 对原生的AJAX进行封装,简化书写。
基本使用
axios 的使用分为以下两步:
-
引入 Axios 的 JS 文件:通过
<script>
标签加载 Axios 库的 JavaScript 文件,使 axios 对象在当前页面可用。html<!-- 1. 引入Axios库 --> <script src="js/axios-0.18.0.js"></script> <!-- 基础依赖 -->
-
使用 Axios 发送请求,并获取响应结果
-
发送 get 请求
jsaxios({ // 创建异步请求 method: "get", // 请求方法 url: "http://localhost:8080/api/data?username=zhangsan" // 带参数的URL }).then(function (resp) { // 成功响应回调 alert(resp.data); // 访问响应数据主体 });
-
发送 post 请求
jsaxios({ method: "post", // 请求方法 url: "http://localhost:8080/api/submit", // 目标URL data: "username=zhangsan" // 请求体数据(表单格式) }).then(function (resp) { // 响应处理回调 alert(resp.data); // 获取服务端返回数据 });
axios() & then()
axios() 方法用于发送异步请求的函数,其小括号内通过 JavaScript 对象传递请求相关参数。
- 结构分析:在axios的调用中,我们创建了一个JS对象,它有多个属性(键对值),并将这个对象作为参数传递给axios函数。(在JS中,
{}
用于创建一个对象) method
属性:用来设置请求方式。常见取值为get
或者post
。url
属性:用来书写请求的资源路径。需将请求参数拼接到路径后面,格式为url?参数名=参数值&参数名2=参数值2
(如url: 'https://example.com/api?name=John&age=30'
)。data
属性:作为请求体被发送的数据。当为post
请求时,数据需作为data
属性的值。
then() 方法用于定义异步操作成功时的执行回调函数。
其在 axios 中的作用:
- 处理成功响应:当 axios 发送的请求成功(HTTP 状态码 2xx),then() 中的回调函数会被触发,接收一个封装响应数据的 resp 对象。
- 数据提取:通过 resp.data 可获取后端返回的实际数据,其他属性如 resp.status (状态码)、resp.headers (响应头)等也可访问
示例:
javascript
// 使用 axios 发送 GET 请求,通过配置对象传递请求参数
axios({
method: 'get', // 设置请求方式为 GET
url: 'https://example.com/api' // 指定请求的资源路径,GET 请求参数需拼接到 URL 后
})
// 通过 then 方法处理成功响应,接收响应对象 resp
.then(function(resp) {
console.log(resp.data); // resp.data 包含后端返回的实际数据,此处打印到控制台
});
快速入门
后端实现
定义一个用于接收请求的servlet,代码如下:
java
@WebServlet("/axiosServlet") // 声明Servlet访问路径
public class AxiosServlet extends HttpServlet {{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {{
System.out.println("get..."); // 标记GET请求入口
//1. 接收请求参数
String username = request.getParameter("username"); // 从请求URL获取参数
System.out.println(username); // 打印参数值(调试用)
//2. 响应数据
response.getWriter().write("hello Axios~"); // 向客户端返回字符串
}}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {{
System.out.println("post..."); // 标记POST请求入口
this.doGet(request, response); // 复用GET处理逻辑
}}
}}
前端实现
- 引入 js 文件
- 发送 ajax 请求
整体页面代码如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Axios 基础示例</title>
</head>
<body>
<!-- 1. 引入 Axios 库(0.18.0版本) -->
<script src="js/axios-0.18.0.js"></script>
<!-- 2. 发送 ajax 请求 -->
<script>
// POST请求示例
axios({
method: "post", // 指定为POST请求方式
url: "http://localhost:8080/ajax-demo/axiosServlet", // 请求目标地址
data: "username=zhangsan" // 请求体数据(表单格式字符串)
}).then(function (resp) { // 请求成功回调函数
alert(resp.data); // 弹出显示服务端返回的数据
});
/*
被注释的GET请求示例(保留参考)
axios({
method: "get", // GET请求方式
url: "http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan" // URL 携带查询参数
}).then(function (resp) { // 成功回调
alert(resp.data); // 处理响应数据
})
*/
</script>
</body>
</html>
请求方法别名
Axios 为每种 HTTP 请求方法提供了专用函数,简化常用请求的编写:
get
请求 :axios.get(url[,config])
post
请求:axios.post(url[,data[,config])
delete
请求 :axios.delete(url[,config])
head
请求 :axios.head(url[,config])
options
请求 :axios.option(url[,config])
put
请求:axios.put(url[,data[,config])
patch
请求:axios.patch(url[,data[,config])
常用 get
请求和 post
请求:
GET 请求别名使用
js
// 原通用方式
axios({
method: "get",
url: "http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan"
}).then(resp => alert(resp.data));
// 简化版(GET别名)
axios.get("http://localhost:8080/ajax-demo/axiosServlet?username=zhangsan")
.then(resp => alert(resp.data));
POST 请求别名使用
js
// 原通用方式
axios({
method: "post",
url: "http://localhost:8080/ajax-demo/axiosServlet",
data: "username=zhangsan"
}).then(resp => alert(resp.data));
// 简化版(POST别名)
axios.post(
"http://localhost:8080/ajax-demo/axiosServlet", // URL
"username=zhangsan" // 请求体数据
).then(resp => alert(resp.data));
JSON
概述
JSON 是什么?
- 全称:JavaScript Object Notation(JavaScript 对象表示法)
- 本质:一种轻量级的数据交换格式
- 起源:基于 JavaScript 对象语法,但独立于编程语言
作用
- 数据载体:在网络传输中承载结构化数据
- 跨平台交换:在不同系统(如 Java 后端和 JavaScript 前端)间传递数据
将 Java 对象数据传输到浏览器
在 Java 开发中,常需将 Java 对象中封装的数据响应回给浏览器。不同的数据传输格式各有特点,其中传统的 XML 方式已逐渐被 JSON 取代。
方式1:简单字符串(局限性大)
缺点:只能传递单一值;无法表示复杂结构(如对象、列表);前端需手动解析复杂数据。
java
// Java 服务端代码
response.getWriter().write("hello Axios~");
方式2:XML(过时的复杂方案)
缺点:格式较为繁琐;标签嵌套会使数据量增大,传输效率相对较低
xml
<!-- Java 对象转换为 XML 格式 -->
<student>
<name>张三</name>
<age>23</age>
<city>北京</city>
</student>
现代数据传输方式:JSON
json
{
"name":"张三",
"age":23,
"city":"北京"
}
上面两种格式进行对比后就会发现 json
格式简单,以及所占的字节数少等优点。
JSON 基础语法
定义格式
JSON 本质是符合特定格式的字符串。
JSON 主要包含两种基本结构,通过它们可以构建任意复杂的数据表示:
类型 | 表示符号 | 描述 | 特点 | 示例 |
---|---|---|---|---|
JSON 对象 | {} |
键值对的无序集合 | 键必须是字符串 | {"name":"张三"} |
JSON 数组 | [] |
值的有序列表 | 元素可混合类型 | ["北京","上海"] |
JSON 的值 可以是以下类型:字符串、数字、布尔值(true/false)、null、对象或数组。这些结构可以相互嵌套、多层嵌套,形成复杂的数据结构。
将 JSON 对象作为字符串 赋值给变量时,需用单引号 ''
或反引号 ```````` 包裹(为了与使用双引号的键名区分)。JSON 对象 定义格式如下:
js
var 变量名 = '{"key":value,"key":value,...}';
JSON 格式规则
-
键(key) 必须是字符串类型 且 使用双引号包裹
-
字符串值 使用双引号包裹
-
数字值 、布尔值(
true
/false
) 不加引号,直接书写 -
空值 使用
null
-
分隔符 :键和值之间使用冒号
:
分隔;键值对之间用逗号,
分隔 -
键名、字符串值 大小写敏感
-
注释不支持:JSON 数据本身(
{}
内)不能含注释,若需要注释,可在代码中处理JSON数据时添加注释。 示例:js// 用户数据JSON结构说明:id: 用户唯一标识(数字);name: 用户名(字符串);roles: 权限列表(数组) const user = JSON.parse('{"id":1,"name":"张三","roles":["admin"]}');
示例:
js
var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}'
代码演示
创建一个页面,在该页面的 <script>
标签中定义json字符串
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
//1. 定义JSON字符串
var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}'
alert(jsonStr);
</script>
</body>
</html>
通过浏览器打开,页面效果如下图所示\
获取JSON对象属性
对于一个 js 对象,可以通过 js对象.属性名
的方式来获取属性数据。JS 提供了一个对象 JSON
,该对象有如下两个方法:
核心转换方法
通过全局对象 JSON
提供的两个方法实现互转:
方法 | 作用 | 示例 |
---|---|---|
JSON.parse(str) | JSON 字符串 → JS 对象 | let obj = JSON.parse(jsonStr) |
JSON.stringify(obj) | JS 对象 → JSON 字符串 | let str = JSON.stringify(obj) |
代码演示:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
//1. 定义JSON字符串
var jsonStr = '{"name":"zhangsan","age":23,"addr":["北京","上海","西安"]}'
alert(jsonStr);
//2. 将 JSON 字符串转为 JS 对象
let jsObject = JSON.parse(jsonStr);
alert(jsObject)
alert(jsObject.name)
//3. 将 JS 对象转换为 JSON 字符串
let jsonStr2 = JSON.stringify(jsObject);
alert(jsonStr2)
</script>
</body>
</html>
发送异步请求携带参数
使用 axios
发送请求携带复杂数据时,通常以 JSON
格式传递。
参数传递方式
1. 手动拼接字符串
- 提前定义一个 js 对象,用来封装需要提交的参数
- 使用
JSON.stringify(js对象)
将 js 对象 转换为JSON
串 - 将该
JSON
串作为axios
的data
属性值进行请求参数的提交
js
var jsObject = {name:"张三"};
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data: JSON.stringify(jsObject)
}).then(function (resp) {
alert(resp.data);
})
2. 直接使用 JS 对象(推荐做法)
- 提前定义一个 js 对象,用来封装需要提交的参数
- 将该 js 对象作为
axios
的data
属性值,axios
会自动将 js 对象转换为JSON
串进行提交
js
var jsObject = {name:"张三"};
axios({
method:"post",
url:"http://localhost:8080/ajax-demo/axiosServlet",
data:jsObject //这里 axios 会将该js对象转换为 json 串的
}).then(function (resp) {
alert(resp.data);
})
注意:
JSON 对象:JS 提供的 JSON 对象简单了解即可,因为 axios 会自动进行 JS 对象和 JSON 串的相互转换。
请求方式:若请求参数是 JSON 格式,请求方式必须为 POST,因为 JSON 串需放在请求体中。
JSON串和Java对象的相互转换
学习完 json 后,接下来聊聊 json 的作用。以后我们会以 json 格式的数据
在进行前后端交互时,前端、后端 需要以 json 格式 发送请求、返回响应 复杂的数据。
所以,需要重点学习以下两部分操作:
- 请求数据:JSON字符串转为Java对象
- 响应数据:Java对象转为JSON字符串
Fastjson 概述
Fastjson 是阿里巴巴提供的一个Java语言编写的高性能功能完善的 JSON
库,是目前Java语言中最快的 JSON
库,可以实现 Java
对象和 JSON
字符串的相互转换。
Fastjson 三步骤快速集成
-
导入坐标
xml<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> <!-- 生产环境建议使用最新稳定版 --> </dependency>
-
Java对象转JSON
javaString jsonStr = JSON.toJSONString(obj);
- 使用
Fastjson
提供的JSON
类中的toJSONString()
静态方法 - 支持所有可序列化的 Java 对象;自动处理嵌套对象和集合类型
-
JSON字符串转Java对象
javaUser user = JSON.parseObject(jsonStr, TargetClass.class);
- 使用
Fastjson
提供的JSON
类中的parseObject()
静态方法 - 参数解析
- jsonString:需要解析的原始 JSON 数据,String类型,
"{"name":"张三"}"
- TargetClass.class:指定转换成哪个类的 Java 对象,Class<T>类型,
User.class
- 返回值:转换后的 Java 对象实例,
User{name='张三'}
- jsonString:需要解析的原始 JSON 数据,String类型,
- 精确控制反序列化目标类型;自动匹配字段名(大小写不敏感)
代码演示
-
引入坐标
-
创建一个类,专门用来测试 Java 对象和 JSON 串的相互转换,代码如下:
javapublic class FastJsonDemo { public static void main(String[] args) { //1. 将Java对象转为JSON字符串 User user = new User(); user.setId(1); user.setUsername("zhangsan"); user.setPassword("123"); String jsonString = JSON.toJSONString(user); System.out.println(jsonString); //{"id":1,"password":"123","username":"zhangsan"} //2. 将JSON字符串转为Java对象 User u = JSON.parseObject( '{"id":1,"password":"123","username":"zhangsan"}', User.class ); System.out.println(u); } }
案例
需求
使用Axios + JSON 完成品牌列表数据查询和添加。页面效果下图所示:
环境准备
导入工程并配置数据库
一、拷贝工程到工作空间
复制 02-AJAX\04-资料\3. 品牌列表案例\初始工程
下的 brand-demo
文件夹,粘贴到个人开发工作空间目录(如 D:\projects
)
二、将工程导入 IntelliJ IDEA
点击 IDEA 菜单栏 File
→ Open
→ 选择 brand-demo
文件夹 → OK
,当右下角提示 Maven projects need to be imported
时,点击 Enable Auto-Import
。导入后工程目录结构如下:
三、修改数据库配置
- 定位到配置文件
src/main/resources/mybatis-config.xml
- 修改数据库连接
-
找到
<dataSource>
部分:xml<dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db_brand?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource>
-
修改以下参数:
url
:改为自己的数据库地址username
:改为自己的数据库用户名password
:改为自己的数据库密码
- 保存更改:按
Ctrl + S
保存文件或点击工具栏「Save All」图标
查询所有功能
查询所有功能 的整体流程
- 前后端需以 JSON 格式进行数据的传递;
- 查询所有的功能,前端发送 ajax 请求不需要携带参数,而后端响应数据需以如下格式的 json 数据:
后端实现
在 com.itheima.web
包下创建名为 SelectAllServlet
的 servlet
,具体的逻辑如下:
- 调用 service 的
selectAll()
方法进行查询所有的逻辑处理 - 将查询到的集合数据转换为 json 数据。我们将此过程称为 序列化 。
序列化 :Java对象 → JSON字符串 ,JSON.toJSONString(obj)
反序列化 :JSON字符串 → Java对象,JSON.parseObject(json, Class)
- 将 json 数据响应回给浏览器,一定要设置响应数据的类型及字符集
response.setContentType("text/json;charset=utf-8");
SelectAllServlet 代码如下:
java
@WebServlet("/selectAllServlet") // 定义Servlet访问路径
public class SelectAllServlet extends HttpServlet {
// 实例化品牌服务层对象
private BrandService brandService = new BrandService();
// GET请求处理
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 调用Service查询所有品牌 → 获取品牌集合
List<Brand> brands = brandService.selectAll();
// 2. 序列化:Java集合 → JSON字符串
String jsonString = JSON.toJSONString(brands); // Fastjson转换
// 3. 设置响应类型为JSON格式 → 字符集UTF-8
response.setContentType("text/json;charset=utf-8"); // 关键响应头
// 4. 将JSON数据写入响应输出流
response.getWriter().write(jsonString); // 返回给前端
}
// POST请求处理(委托给GET)
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response); // 统一处理逻辑
}
}
前端实现
- 引入Axios库的 js 文件
在brand.html
页面引入 Axios 的 js 文件 - 绑定
页面加载完毕
事件
在brand.html
页面绑定加载完毕事件,该事件在页面所有资源(HTML/CSS/图片)加载完成后执行 - 发送异步请求
在页面加载完毕事件 绑定的匿名函数中 发送异步请求 - 处理响应数据
初始 :在then
回调中,通过resp.data
获取响应回来的数据(品牌数据数组)。这个数组的每个元素是一个品牌对象,包含brandName
、companyName
等属性,如下:
目标 :将 响应回来的数据(品牌数据数组)整理为HTML表格形式
方法:处理从服务器获取的品牌数据,并将这些数据动态地展示在HTML表格中。 步骤:
- 首先,表头行是固定的,所以先定义初始值是表头行数据的字符串。(表头
thead
,也就是表格的第一行tr
,包含"序号"、"品牌名称"等列标题) - 然后,遍历从服务器返回的品牌数据数组(
brands
)。获取每一个品牌对象,并生成其表格行(tr
),每一行中的每个单元格(td
)填充该品牌对象的相应属性 - 将这些动态生成的行(字符串形式)依次拼接到之前定义的表头字符串后面,形成完整的表格HTML内容。
- 最后,通过
document.getElementById("brandTable").innerHTML = 拼接好的字符串
将完整的表格HTML字符串设置为指定表格元素(ID为"brandTable"
)的innerHTML(内部HTML),就可以动态地展示出用户想看到的数据 关于document.getElementById("DOM元素ID").innerHTML = 拼接好的字符串
的详解该语句用于将动态生成的 HTML 内容插入到指定 DOM 元素中。具体来说:
document.getElementById("DOM元素ID")
:通过 ID 获取页面中的指定 DOM 元素。.innerHTML
:操作该元素的 HTML 内容,支持字符串拼接的 HTML 代码注入。- 赋值操作:将拼接好的字符串直接渲染为表格内容,覆盖原有内容。
整体页面代码如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="addBrand.html"><input type="button" value="新增"></a><br>
<hr>
<table id="brandTable" border="1" cellspacing="0" width="100%">
</table>
<!-- 1.引入axios库 -->
<script src="js/axios-0.18.0.js"></script>
<script>
// 2.页面加载完成后执行
window.onload = function () {
// 3.发送AJAX请求获取品牌数据
axios({
method:"get",
url:"http://localhost:8080/brand-demo/selectAllServlet"
}).then(function (resp) { // 请求成功后的回调
// 4.处理响应数据
let brands = resp.data; // 获取响应数据中的品牌列表
// 4.1 初始化表头行数据
let tableData = " <tr>\n" +
" <th>序号</th>\n" +
" <th>品牌名称</th>\n" +
" <th>企业名称</th>\n" +
" <th>排序</th>\n" +
" <th>品牌介绍</th>\n" +
" <th>状态</th>\n" +
" <th>操作</th>\n" +
" </tr>";
// 4.2 遍历所有品牌数据
for (let i = 0; i < brands.length ; i++) {
let brand = brands[i]; // 获取单个品牌对象
// 4.3 拼接表格行数据
tableData += "\n" +
" <tr align=\"center\">\n" +
" <td>"+(i+1)+"</td>\n" +
" <td>"+brand.brandName+"</td>\n" +
" <td>"+brand.companyName+"</td>\n" +
" <td>"+brand.ordered+"</td>\n" +
" <td>"+brand.description+"</td>\n" +
" <td>"+brand.status+"</td>\n" +
"\n" +
" <td><a href=\"#\">修改</a> <a href=\"#\">删除</a></td>\n" +
" </tr>";
}
// 4.4 将拼接好的HTML插入表格元素内
document.getElementById("brandTable").innerHTML = tableData;
})
}
</script>
</body>
</html>
添加品牌功能

如上所示
- 用户点击 "新增" 按钮会跳转到 添加页面(
addBrand.html
) - 在 添加页面 填写表单数据后点击 "提交" 按钮,前端将数据以JSON格式提交到后端
- 后端将数据保存到数据库
具体的前后端交互的流程如下:
后端实现
在 com.itheima.web
包下创建名为 AddServlet
的 servlet
,具体的逻辑如下:
- 通过
HttpServletRequest
对象获取请求体的字符输入流(前端提交的是 JSON 格式数据,无法用获取键值对参数的getParameter()
方法获取),再从这个输入流中读取JSON字符串 - 使用 Fastjson 将 JSON 字符串反序列化为 Java
Brand
对象 - 获取 BrandService 实例,以调用Service层的
add()
方法,将Brand
对象作为参数传入add(Brand brand)
- 通过响应字符串
"success"
来通知前端操作成功(但这种方式在复杂场景下不够灵活,实际开发建议使用JSON格式响应)
AddServlet 代码如下:
java
@WebServlet("/addServlet")
public class AddServlet extends HttpServlet {
private BrandService brandService = new BrandService(); // 实例化品牌服务对象
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.获取请求体数据
BufferedReader br = request.getReader(); // 获取请求体的字符输入流
String params = br.readLine(); // 读取请求体数据(JSON格式)
// 2.转换JSON数据为Java对象
Brand brand = JSON.parseObject(params, Brand.class);
// 3.调用service层添加品牌
brandService.add(brand); // 将品牌对象持久化到数据库
// 4.返回操作成功的响应
response.getWriter().write("success"); // 向客户端发送成功标识
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
前端实现
- 引入Axios库的 js 文件
- 给提交按钮绑定点击事件,在事件处理函数中:
a. 创建一个js对象formData
,用于封装表单数据(注意:每次点击都要重新获取最新的表单数据,所以对象应该定义在事件处理函数内部),内部设置属性初始值均为空字符串""
,作为数据占位符。
b. 获取各个表单元素的值,并通过formData.X = 新值
实现对应属性值的更新,初始空字符串被用户输入值替换。
c. 对于单选框按钮,使用循环判断哪个被选中,然后获取其值。 - 使用axios发送异步请求,将
formData
对象转换为后端可解析的JSON字符串,设置请求头为application/json
告知服务器数据类型 - 处理响应,如果返回
"success"
,则跳转到brand.html页面。
整体页面代码如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加品牌</title>
</head>
<body>
<h3>添加品牌</h3>
<form action="" method="post">
品牌名称:<input id="brandName" name="brandName"><br>
企业名称:<input id="companyName" name="companyName"><br>
排序:<input id="ordered" name="ordered"><br>
描述信息:<textarea rows="5" cols="20" id="description" name="description"></textarea><br>
状态:
<input type="radio" name="status" value="0">禁用
<input type="radio" name="status" value="1">启用<br>
<input type="button" id="btn" value="提交">
</form>
<!-- 1.引入axios库 -->
<script src="js/axios-0.18.0.js"></script>
<script>
// 2.给按钮绑定单击事件
document.getElementById("btn").onclick = function () {
// 将表单数据转为json
var formData = {
brandName:"",
companyName:"",
ordered:"",
description:"",
status:"",
};
// 获取表单数据,并设置数据
let brandName = document.getElementById("brandName").value;
formData.brandName = brandName;
let companyName = document.getElementById("companyName").value;
formData.companyName = companyName;
let ordered = document.getElementById("ordered").value;
formData.ordered = ordered;
let description = document.getElementById("description").value;
formData.description = description;
let status = document.getElementsByName("status");
// 获取单选按钮的值
for (let i = 0; i < status.length; i++) {
if(status[i].checked){
//
formData.status = status[i].value ;
}
}
// 3.发送ajax请求
axios({
method:"post",
url:"http://localhost:8080/brand-demo/addServlet",
data: JSON.stringify(formData), // 转为JSON字符串
headers: { // 添加请求头,设置内容类型
"Content-Type": "application/json"
}
}).then(function (resp) {
// 4.处理响应
if(resp.data == "success"){
location.href = "http://localhost:8080/brand-demo/brand.html";
}
})
}
</script>
</body>
</html>
数据获取部分简化:
html
<script>
const formData = {
brandName: document.getElementById("brandName").value,
companyName: document.getElementById("companyName").value,
ordered: document.getElementById("ordered").value,
description: document.getElementById("description").value,
status: document.querySelector('input[name="status"]:checked').value // 优化单选按钮获取
};
</script>
分析
- 单行代码完成多个操作(简洁语法):
在对象字面量内部直接通过表达式(如document.getElementById(...).value
)获取值并赋给对象的属性。 - CSS选择器的高级用法:
在document.querySelector
中使用了复合选择器:input[name="status"]:checked
,表示选择name属性为"status"且被选中的input元素。