一、过滤器
过滤器(Filter)是一个用于对请求和响应进行预处理的组件。过滤器可以在 Java Servlet 规范中使用,通常用于执行一些通用的任务
1、过滤器的作用
过滤器是一种javaEE规范中定义的一种技术,可以让请求达到目标servlet之前,先进入过滤器进行一些拦截处理
当处理完成后,可以机洗向后执行,到达目标servlet。如果配置了多个过滤器,也可以进入到下一个过滤器
2、过滤器的使用场景
(1)统一编码过滤
统一编码过滤器确保所有进入的请求和所有返回的响应都使用统一的字符编码(如 UTF-8)
一般所有的请求和响应都需要通过此过滤器,所以建议在配置时配置为全部域可以进入到此过滤器
(2)权限验证
权限验证过滤器用于检查用户是否有权限访问特定的资源。这对于确保安全性非常重要
(3)跨域过滤
一般情况下浏览器不允许跨域。所谓跨域,就是指在不同服务之间进行访问,在访问时,只要请求协议、域名、端口其中之一不同,就属于跨域访问,在后端通过跨域过滤器响应时,过滤器告知本次响应安全,可以正常接收,从而解决跨域问题
3、过滤器的搭建
(1)统一编码过滤器的搭建
跨域过滤器功能组件(类)
package com.wbc.dormServer.filter;
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
String reqencod="";
String respencod="";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化过滤器");
reqencod=filterConfig.getInitParameter("reqencod");
respencod=filterConfig.getInitParameter("respencod");
}
@Override
public void destroy() {
System.out.println("过滤器销毁时执行");
}
//执行过滤操作的方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("编码过滤器");
//设置请求编码集
servletRequest.setCharacterEncoding(reqencod);
//设置响应编码集
servletResponse.setContentType(respencod);
//让请求离开过滤器,继续向下执行,下一个可能是过滤器,也可能是目标访问的servlet
filterChain.doFilter(servletRequest, servletResponse);
}
}
过滤器的配置(配置于webapp/WEB-INF/web.xml)
<!-- 注册编码过滤器-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.wbc.dormServer.filter.EncodingFilter</filter-class>
<init-param>
<param-name>reqencod</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>respencod</param-name>
<param-value>text/html;charset=utf-8</param-value>
</init-param>
</filter>
<!-- 配置那些地址可以进入到过滤器-->
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern><!--/*表示所有向后端发送的请求都要进入编码过滤器-->
</filter-mapping>
此处将例如reqencod(utf-8)、respencod(text/html;charset-utf-8)等参数配置于web.xml是便于打jar包后修改此参数
打包后,web.xml可以直接进行修改,而java文件不可以(因为已经编译为class文件)。特在此说明
(2)跨域过滤器
跨域过滤器功能组件(类)
package com.wbc.dormServer.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class CorsFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
//允许携带Cookie时不能设置为* 否则前端报错
//httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("origin"));//允许所有请求跨域
httpResponse.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8848");//允许已知安全可靠的另一个服务的请求跨域
httpResponse.setHeader("Access-Control-Allow-Methods", "*");//允许跨域的请求方法GET, POST, HEAD 等
httpResponse.setHeader("Access-Control-Allow-Headers", "*");//允许跨域的请求头
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//是否携带cookie
filterChain.doFilter(servletRequest, servletResponse);
}
}
此处通过@WebFilter(urlPatterns = "/*") 在类中配置,便不需要在web.xml文件中配置
说明:
@WebFilter(urlPatterns = "/供外界访问的地址") 是 Java EE(现在的 Jakarta EE)中一种注解配置方式,用于定义一个 Servlet 过滤器。它通过使用注解来简化配置,避免了修改web.xml 文件的需要。
相应的,配置servlet是也可以通过类似方法进行配置,如此时需要配置一个检查类,可以通过注释@WebServlet(urlPatterns = "/check",name = "check", loadOnStartup = 1)来简化配置
二、前后端交互、发送请求
前后端交互发送请求一般分为两种,一种是同步请求(已淘汰),另一种是异步请求
1、同步请求
(1)同步
简单来说,同步就是一次只能做一件事
(2)同步请求
知道了同步,同步请求就很好理解了,当前端像后端发送请求时,此时客户端一切操作都会终止,服务器相应回来的内容会覆盖当前网页的内容。一次只能做一件事,与服务器交互时其他事情不能再继续。例如在表单、超链接发送请求时,都是同步请求,其相应会覆盖当前的网页内容
示例:
以一个简单的登录html为例
在同步请求时,后端反馈的登录成功会覆盖掉之前的网页,十分的不简便不美观
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="http://127.0.0.1:8182/dormServer/login" method="post">
账号:<input type="text" name="account" value=""/><br />
密码:<input type="password" name="password" value=""/><br />
<input type="submit" value="登录"/>
</form>
</body>
</html>
服务器响应类
package com.wbc.dormServer.web;
import com.wbc.dormServer.dao.LoginDao;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.sql.SQLException;
public class LoginServlet extends HttpServlet {
/*处理get请求*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet()");
String name = req.getParameter("name");
String age = req.getParameter("age");
System.out.println(name);
System.out.println(Integer.parseInt(age));
}
/*处理post请求*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost()");
//设置请求内容解码的编码
//req.setCharacterEncoding("utf-8");
String account = req.getParameter("account");
String password = req.getParameter("password");
System.out.println(account+" "+password);
//调用jdbc
LoginDao loginDao = new LoginDao();
boolean res = false;
//resp.setContentType("text/html;charset=utf-8");//设置相应内容的编码
PrintWriter printWriter = resp.getWriter();//获得一个打印输出流
try {
res = loginDao.login(account, password);
if (res){
//向前做出相应
printWriter.write("<h2>登陆成功</h2>");
}
else if (!res){
//向前做出相应
printWriter.write("<h2>账号不存在</h2>");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
printWriter.write("<h2>网络异常404</h2>");
}
}
}
JDBC 与数据库交互
package com.wbc.dormServer.dao;
import java.sql.*;
public class LoginDao {
public boolean login(String username, String password) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url ="jdbc:mysql://127.0.0.1:3306/chatdb?serverTimezone=Asia/Shanghai";//定义连接sql所需的url
String users ="root";//用户名
String passwords ="Wbc11280";//密码
//建立连接
Connection connection = DriverManager.getConnection(url,users,passwords);//建立连接
//预编译
PreparedStatement preparedStatement =connection.prepareStatement("select account from user where account = ? and password = ?");
//传入数据
preparedStatement.setObject(1, username);
preparedStatement.setObject(2, password);
//查询操作
ResultSet resultSet = preparedStatement.executeQuery();//将查询结构封装到ResultSet类型的对象中 需要将数据封装到指定类型的对象中
/*preparedStatement.close();
connection.close();*/
return resultSet.next();
}
}
2、异步请求
(1)异步
异步与同步相反,简单来说,异步就是同时可以做多件事
(2)异步请求
当客户端与服务器交互时,不影响客户端页面的其他操作,同时做多件事情
服务器相应回来的内容不会覆盖整个页面,现在的前后端交互都是异步的,由于页面不用覆盖,体验感更好
实现发送异步请求
实现发送异步请求有两种方法,一种是较为原始的在前端用一个js对象 XMLhttpRequest 发送请求、接收响应的方式;另一种是通过异步框架来实现,但底层逻辑也是通过XMLhttpRequest来实现
(1)在前端用一个js对象 XMLhttpRequest 发送请求、接收响应
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
function checkAccount(account){
//location.href = "http://127.0.0.1:8182/dormServer/check?account="+account;
/*
跨域访问问题
在8848的服务访问8182后端服务
默认浏览器不允许接收来自另一个服务的相应内容
认为相应回来的数据时来自另一个服务,可能不安全
*/
//1、创造请求对象
var httpObj= new XMLHttpRequest();
httpObj.open("get","http://127.0.0.1:8182/dormServer/check?account="+account,true)//封装请求
httpObj.send(null);//发送请求
//2、执行回调函数,接收响应结果
httpObj.onreadystatechange=function(){
document.getElementById("msg").innerHTML=httpObj.responseText;
}
}
</script>
</head>
<body>
<input type="text" name="account" onblur="checkAccount(this.value)"/><span id="msg"></span><br/>
<input type="password" />
</body>
</html>
后端响应
package com.wbc.dormServer.web;
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;
@WebServlet(urlPatterns = "/check",name = "check", loadOnStartup = 1)
public class CheckAccountServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String account = req.getParameter("account");
if (account.equals("aaaa")) {
resp.getWriter().write("账号已存在");
}
else {
resp.getWriter().write("账号未注册");
}
}
}
实现效果 :输入账户结束鼠标指针移开后给出提示,不覆盖当前页面
(2)通过异步请求框架,这里以axios框架为例
axios中文网|axios API 中文文档 | axios (axios-js.com)axios框架官网:axios中文网|axios API 中文文档 | axios (axios-js.com)
在官网下载好js文件后导入到文件夹
此处以发送get请求为例,通过axios对异步请求的封装,使得在写代码时更加的方便高效。现在普遍在项目开发中使用异步请求框架。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/axios.min.js"></script>
<script >
function checkAccount(account){
/* axios框架对异步请求的封装 */
axios.get("http://127.0.0.1:8182/dormServer/check?account="+account).then((resp)=>
{
console.log("msg");
document.getElementById("msg").innerHTML=resp.data;//取出后端相应的内容
});
}
</script>
</head>
<body>
<input type="text" name="account" onblur="checkAccount(this.value)"/><span id="msg"></span><br/>
<input type="password" />
</body>
</html>
需要注意的是,当你的应用可能会遇到跨域请求时,配置适当的跨域过滤器是必要的,无论请求是同步请求还是异步请求。
三、json以及后端响应json数据
当后端向前端响应数据时,总是希望可以通过对象进行封装。而不同语言之间是无法直接传输对象的。js想要接收到java的数据只能通过字符串来传输。而无论对于前端程序员还是后端程序员来说,拼接和分离字符串都是一件令人头疼的事件。并且在拼接和分离字符串中也需要明确的规则来进行操作,否则可能会出现数据丢失或其他不必要的麻烦,因此json孕育而生。
1、json的概述
json(Java javaScript object Notation)javaScript对象表示法,是一种js对象表示方式的字符串。目前json格式已成为公认的前后端交互的数据标准格式
2、json的作用
后端向前端响应更多的数据,后端一般情况下将数据封装到对象中,但是js不认识java对象。java中toString将对象转为字符串,js接到后却无法用面向对象的方法直接使用。为了是js更方便的进行操作,在java中将对象转为json格式的字符串,传递给前端js
3、json的运用(示例)
我们以查询一个学生信息为例
v1.0
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/axios.min.js"></script>
<script>
function search(){
var name = document.getElementById("name").value;
axios.get("http://127.0.0.1:8182/dormServer/search?name="+name).then((resp)=>{
console.log(resp.data);
document.getElementById("numid").innerHTML=resp.data.num;
document.getElementById("nameid").innerHTML=resp.data.name;
document.getElementById("genderid").innerHTML=resp.data.gender;
document.getElementById("ageid").innerHTML=resp.data.age;
});
}
</script>
</head>
<body>
<input type="text" id="name"/><input type="button" value="搜素" onclick="search()"/>
<div>
学号:<span id="numid"></span><br />
姓名:<span id = "nameid"></span><br />
性别:<span id = "genderid"></span><br />
年龄:<span id = "ageid"></span><br />
</div>
</body>
</html>
学生模组
package com.wbc.dormServer.model;
public class Student {
private int num;
private String name;
private int age;
private String gender;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"num=" + num +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
后端相应
package com.wbc.dormServer.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wbc.dormServer.model.Student;
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;
@WebServlet(urlPatterns = "/search",name = "search", loadOnStartup = 1)
public class SearchServlet_1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
//模拟从数据库查询学生信息,并封装到一个学生对象中
Student student = new Student();
student.setNum(100);
student.setName(name);
student.setAge(20);
student.setGender("男");
/*
* json Java javaScript object Notation javaScript对象表示法 {name=aaa,age=20}
* json 是一种公认的 js识别的对象表示方式 对于java来说就是一种特殊的固定格式的字符串
*
* 对象:{键:值,键:值,...}
* 集合:[{键:值,键:值,...},{键:值,键:值,...},...]
* */
//String s = "{name:'"+student.getName()+"',age:"+student.getAge()+"}";
//打印响应一个学生对象
PrintWriter writer = resp.getWriter();
ObjectMapper objectMapper = new ObjectMapper();
//通过json组件将java对象转为json格式的字符串
String jsonString = objectMapper.writeValueAsString(student);
writer.print(jsonString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
在正规项目中,一般打包发送状态码code,提示信息desc,以及数据。因此我们在此将上方代码规范化
v2.0
添加一个标准的响应模型封装类
package com.wbc.dormServer.model;
//标准的响应模型封装类
public class Result {
private int code;
private String message;
private Object data;
public Result(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
修改服务器响应类,改为发送标准响应模型封装类,并做异常处理
package com.wbc.dormServer.web;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wbc.dormServer.model.Result;
import com.wbc.dormServer.model.Student;
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;
@WebServlet(urlPatterns = "/search",name = "search", loadOnStartup = 1)
public class SearchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
Result result = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
String name = req.getParameter("name");
//模拟从数据库查询学生信息,并封装到一个学生对象中
Student student = new Student();
student.setNum(100);
student.setName(name);
student.setAge(20);
student.setGender("男");
result= new Result(200, "查询成功", student);
}
catch (Exception e) {
result = new Result(500, "查询失败",null );
}
//通过json组件将java对象转为json格式的字符串
String jsonString = objectMapper.writeValueAsString(result);
writer.print(jsonString);
/*
*//*
* json Java javaScript object Notation javaScript对象表示法 {name=aaa,age=20}
* json 是一种公认的 js识别的对象表示方式 对于java来说就是一种特殊的固定格式的字符串
*
* 对象:{键:值,键:值,...}
* 集合:[{键:值,键:值,...},{键:值,键:值,...},...]
* *//*
//String s = "{name:'"+student.getName()+"',age:"+student.getAge()+"}";
//打印响应一个学生对象
PrintWriter writer = resp.getWriter();
ObjectMapper objectMapper = new ObjectMapper();
//通过json组件将java对象转为json格式的字符串
String jsonString = objectMapper.writeValueAsString(student);
writer.print(jsonString);*/
}
}
相应的修改前端代码,以匹配后端响应代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/axios.min.js"></script>
<script>
function search(){
var name = document.getElementById("name").value;
axios.get("http://127.0.0.1:8182/dormServer/search?name="+name).then((resp)=>{
console.log(resp.data);
if(resp.data.code==200){
document.getElementById("numid").innerHTML=resp.data.data.num;
document.getElementById("nameid").innerHTML=resp.data.data.name;
document.getElementById("genderid").innerHTML=resp.data.data.gender;
document.getElementById("ageid").innerHTML=resp.data.data.age;
document.getElementById("showMsg").innerHTML=resp.data.message;
}
else if(resp.data.code==500){
document.getElementById("showMsg").innerHTML=resp.data.message;
}
});
}
</script>
</head>
<body>
<input type="text" id="name"/><input type="button" value="搜素" onclick="search()"/>
<div>
<div id = "showMsg"></div>
学号:<span id="numid"></span><br />
姓名:<span id = "nameid"></span><br />
性别:<span id = "genderid"></span><br />
年龄:<span id = "ageid"></span><br />
</div>
</body>
</html>
效果: