【JavaWeb开发】前后端交互万字总结
本篇文章包含了JavaWeb开发前后端交互的所有基础知识点,包括服务器的概念、Servlet的创建和使用,Http请求、过滤器的使用、同步和异步请求、跨域问题的解决、Axios框架发送异步请求、路由导航守卫、Web会话跟踪。
目录
一. 服务器
▐ 什么是服务器?
服务器是一个容器,是一个连接用户与程序之间的中间件,严格意义上的服务器指的是一款软件,里面可以放置文件,让别人可以远程访问,并且有请求必有响应。
▐ Tomcat服务器搭建?
关于Tomcat服务器的搭建和部署在之前的文章已经写到,这里不再阐述,不会的小伙伴可以参考此链接文章:
二. Servlet程序
▐ 什么是Servlet?
• Servlet程序也就是我们运行在服务器端的Java程序.
▐ Servlet的创建和使用?
• 创建一个类并继承HttpServlet,这样我们的类也就遵守了JavaEE规范
• 重写父类中的方法(post请求就重写doPost方法,get请求就重写doGet方法)
• 在web.xml文件中配置Servlet
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("init");
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("service");
}
@Override
public void destroy() {
System.out.println("destory");
}
}
<!-- 注册Servlet-->
<servlet>
<servlet-name>login</servlet-name> <!-- 为Servlet对象定义名称-->
<servlet-class>webserver.servlet.LoginServlet</servlet-class>
</servlet>
<!-- 为Servlet配置访问地址-->
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern> <!-- 前端访问地址,必须/开头-->
</servlet-mapping>
▐ Servlet的方法和生命周期?
• 无参构造: 用来初始化Servlet,可以不写,只被调用一次.
• init( ): 也是用来初始化Servlet的,在构造方法执行完毕后执行,只执行一次。
• server( ): 多次被执行,是提供服务的.
• destory( ): 在服务器关闭时调用.
▐ Servlet程序的作用?
我们的后端Servlet程序主要做三件事:
接收前端请求,调用dao层与数据库交互,最后向前端做出响应即可。
三. http请求
请求中包含:
http请求的两种方式:
▐ get请求
- 数据是作为 URL 的一部分进行传输的,通常附加在 URL 地址之后,以 “?” 分隔 URL 和传输数据,多个参数之间用 “&” 连接。
- 例如:
https://www.example.com/search?q=keyword&page=2
,其中 “q=keyword&page=2” 就是通过 GET 方式传递的参数。 - 常用于获取 服务器上的资源,如获取网页内容、查询数据等;适用于对安全性要求不高、数据量较小的请求,比如搜索引擎的查询。
▐ post请求
- 数据是放在 HTTP 请求的请求体中进行传输的,不会显示在 URL 中。
- 这样对于一些敏感信息或者大量数据的传输,POST 方式相对更安全和合适。
- 通常用于向服务器提交 数据,进行数据的添加、修改、删除等操作;比如用户注册、提交表单、上传文件等场景。
一次请求的流程:
四. 过滤器
▐ 什么是过滤器?
过滤器是JavaEE规范中定义的一种技术,可以让请求达到目标Servlet之前,先进入到过滤器中,在过滤器中统一进行一些拦截处理,当处理完成后,可以继续向后执行到达目标Servlet,如果配置了多个过滤器,也可以进入到下一个过滤器。
一个过滤器可以配置给多个资源使用,一个资源也可以配置多个过滤器,按照配置顺序调用。
▐ 过滤器的作用?
提高代码复用性,提高可维护性。
▐ 过滤器的使用场景?
统一编码过滤、权限验证、跨域过滤。
▐ 代码实现?
➱ 创建过滤器(以编码过滤器为例)
结合类名,让这个类实现 Filter接口,并重写 Filter接口中的抽象方法,在 doFilter方法中设置字符集,并让过滤器继续向后执行。
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("编码过滤器");
//设置请求编码集
servletRequest.setCharacterEncoding("utf-8");
//设置响应编码集
servletResponse.setContentType("text/html;charset=utf-8");
//让请求离开过滤器继续向下执行,下一个可能是过滤器或目标访问的servlet
filterChain.doFilter(servletRequest,servletResponse);
}
}
➱ 在web.xml文件中配置过滤器
<!-- 注册编码过滤器-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>stuServer.filter.EncodingFilter</filter-class>
</filter>
<!-- 配置哪些地址可以进入到编码过滤器-->
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!-- /* 表示所有向后端发送的请求,都进入到编码过滤器中-->
</filter-mapping>
通过编码过滤器的设置,我们就不需要在每个Servlet接收post请求时都设置解码字符编码了,前端发送的请求会先进入到我们的编码过滤器,提高代码复用性.
五. 同步请求与异步请求
同步请求
之前在网页中通过超链接或者表单就可以向后端发送请求,后端也可以正常响应内容,但当网页与后端交互时,前端不能再进行其他操作,服务器端响应回来的内容会把整个浏览器中的内容覆盖掉,这种请求方式称为同步请求。
例如登录案例,同步请求响应的结果会将浏览器原本的登录界面覆盖掉。
这种请求方式在前后端交互时显得很不友好,现在的前后端交互请求都使用的是异步请求。
异步请求
• 异步请求则同时可以做多件事情,前端与服务器交互时,不影响前端网页其他操作。
• 异步请求是使用js中提供的XMLHtttpRequest对象实现发送异步请求和接受服务器响应的。
<script>
function checkAccount(account){
//异步请求,使用js对象发送请求
var httpobj = new XMLHttpRequest();
//封装请求地址和数据
httpobj.open("get","http://127.0.0.1:8088/stuServer/reg?account="+account,true);
//发送请求
httpobj.send();
//接收请求
httpobj.onreadystatechange=function(){
document.getElementById("msg").innerHTML = httpobj.responseText;//获取到响应的内容
}
}
</script>
但是我们会发现这样做之后还是不能实现我们想要的结果,浏览器会报错,这个错也就是接下来将要提到的跨域问题。
▐ 跨域问题
异步请求时,会出现跨域访问问题:也就是浏览器默认不允许js对象接收来自其他服务器响应的内容,跨域问题是一个前端问题,既可以在前端解决,也可以在后端解决。
▐ 什么是跨域?
• 跨域是指从一个域名的网页去请求另一个域名的资源。
• 比如从 www.baidu.com页面去请求 www.google.com 的资源。但是一般情况下不能这么做,它是由浏览器的同源策略造成的,是浏览器对 JavaScript 施加的安全限制。
• 所谓同源是指:域名,协议,端口均相同,只要有一个不同,就是跨域。
• 跨域的严格一点的定义是:只要协议,域名,端口有任何一个的不同,就被当作是跨域。
▐ 为什么要跨域访问?
由于现在的前后端分离开发,前端和后端各自在不同的服务站,同一家公司不同项目之间要交互,比如一个location.company.com,而应用是放在app.company.com,这时想从app.company.com去访问location.company.com的资源就属于跨域。
▐ 浏览器为什么要阻止跨域访问?
为了安全,不能让其他服务器的内容肆意的响应到自己的服务中.
后端解决
➱ 具体操作如下:
在后端添加一个跨域过滤器,这样后端在向前端响应时,就会告诉浏览器我们本次响应是安全的.
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-Methods", "*");//允许跨域的请求方法GET, POST, HEAD 等
httpResponse.setHeader("Access-Control-Allow-Headers", "*");//允许跨域的请求头
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//是否携带cookie
filterChain.doFilter(servletRequest, servletResponse);
}
}
解决跨域问题后,就可以通过异步请求来达到我们想要的效果了:
▐ Axios框架
异步请求的原始写法比较复杂,所以我们通过Axios框架来封装简化这部分代码。
官方网址:
在普通html中导入axios
进入官网选择使用cdn下载 :
如图访问axios.min.js文件,将此文件下载下来,在html文件中导入即可。
<script src="js/axios.min.js"></script>
在vue项目中安装axios
1. 在vue项目中通过指令安装axios
npm install axios
📖 若安装过程出现卡顿,可以在命令行中更换镜像源,这里推荐使用华为云镜像源,本人亲测好用,检查更换成功后再通过命令安装就可以了。
//使用华为云镜像源加速 NPM npm config set registry https://mirrors.huaweicloud.com/repository/npm/ //查看当前的镜像源 npm config get registry
2. 导入axios
import axios from 'axios';
3. 定义后端基本的地址,后面发送请求时只需要写Servlet地址即可,自动将这部分拼接过去
axios.defaults.baseURL = "http://127.0.0.1:8088/dorm/";
4. 将axios对象挂载到vue对象中,$http是在vue对象中自定义的属性
Vue.prototype.$http = axios;
六. JSON数据格式
▐ 概念
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,后端一般情况下将数据封装到对象中,但是 js不认识 java对象。java中toString方法将对象转为字符串,js接收到后只是普通的字符串,不能使用面向对象的方式操作,为了让 js能够方便的进行操作,需要在 java中将对象转为json格式的字符串,传递给前端 js,目前json格式已成为公认的前后端交互的标准数据格式。
json是一种公认的 js识别的对象表示方式,对于java而言就是一种特定格式的字符串;
所以简单来说,json就是一种固定格式的字符串
- 对象: { 键:值 , 键:值 , ...}
- 集合:[ { 键:值 , 键:值 , ...} , { 键:值 , 键:值 , ...} , ...]
▐ Jackson 库中的关键类和方法
ObjectMapper
是 Jackson 库中的核心类,主要用于 Java 对象和 JSON 之间的相互转换。它提供了一系列的方法来实现不同格式的数据转换和处理。writeValueAsString
方法的作用是将一个 Java 对象序列化为 JSON 字符串格式。用于将 Java 对象序列化为 JSON 字符串进行传输。
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonExample {
public static void main(String[] args) throws Exception {
// 创建一个简单的 Java 对象
Person person = new Person("Alice", 30);
// 创建 ObjectMapper 实例
ObjectMapper objectMapper = new ObjectMapper();
// 使用 writeValueAsString 方法将 Java 对象转换为 JSON 字符串
String jsonString = objectMapper.writeValueAsString(person);
System.out.println(jsonString);
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 省略 getter 和 setter 方法
}
➱ 我们要将数据转换成json格式,需要在后端pom.xml文件中导入jackson组件
<!-- jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
七. 路由导航守卫
▐ 添加路由导航守卫的作用?
路由导航守卫是 Vue Router 提供的一种强大的机制,用于控制路由的导航过程。
可以根据用户的登录状态、角色等信息来决定是否允许用户访问某个路由,例如,当用户未登录时,阻止其访问需要登录权限的页面,并将其重定向到登录页面。
▐ 代码实现
在前端vue项目的index.js中添加一个路由导航守卫。
//配置路由导航守卫,每当进行一次组件路由时,自动执行此段代码
rout.beforeEach((to, from, next) => {
if (to.path == '/login') { //访问的路由地址
return next(); //继续正常访问目标地址
} else {
var token = window.sessionStorage.getItem("account");//获取到浏览器中存储的管理员信息
if (token == null) { //如果信息为空,说明没有登录
return next("/login");
} else { //说明已经登录,可以正常访问
next();
}
}
})
📖全局前置守卫(beforeEach)
- 在路由切换之前被调用。接收三个参数:to(即将要进入的目标路由对象)、from(当前导航正要离开的路由对象)、next(函数,用于决定是否继续导航)。
- 可以在这个守卫中进行权限验证、全局数据预取等操作。如果 next () 没有被调用,导航将被中断。
八. Web会话跟踪
▐ 为什么需要会话跟踪?
因为http请求是无状态的,不携带用户信息,当用户登录成功后,之后再次与服务器交互时,服务器并不知道是哪个用户发送的请求,所以在Web开发中,必须要解决此问题,即之后的请求还需要让后端知道是哪个账号发送的请求,而会话跟踪就是解决此问题的。
▐ 理论流程
▐ 代码实现
1. 在后端中pom.xml文件中导入JWT组件
<!-- JWT组件-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.2</version>
</dependency>
➱ JWT是生成token的一种组件,它生成的token有三部分:
• 第一部分:生成类型和加密算法.(声明)
• 第二部分:载荷,包含用户信息.(id,账号,头像)
• 第三部分:加密生成的的,也是最重要的.
2. 在后端添加JWT工具类,让工具为我们生成一个token字符串。
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ffyc.dormserver.model.Admin;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类
*/
public class JWTUtil {
/**
* 根据用户id,账号生成token
* @param
* @return
*/
public static String getToken(Admin admin) {
String token = "";
try {
//过期时间 为1970.1.1 0:0:0 至 过期时间 当前的毫秒值 + 有效时间
Date expireDate = new Date(new Date().getTime() + 3600*1000);
//秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
//设置头部信息
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
//携带id,账号信息,生成签名
token = JWT.create()
.withHeader(header)//第一部分
.withClaim("id",admin.getId())//第二部分
.withClaim("account",admin.getAccount())//第二部分
.withExpiresAt(expireDate)//第二部分
.sign(algorithm);//第三部分:秘钥
}catch (Exception e){
e.printStackTrace();
return null;
}
return token;
}
/**
* 验证token是否有效
* @param token
* @return
*/
public static boolean verify(String token){
try {
//验签
Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception e) {//当传过来的token如果有问题,抛出异常
return false;
}
}
/**
* 获得token 中playload部分数据,按需使用
* @param token
* @return
*/
public static DecodedJWT getTokenInfo(String token){
return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
}
}
3. 在前端vue项目main.js中添加请求拦截器
//axios 请求拦截
axios.interceptors.request.use(config => {
//为请求头对象,添加 Token 验证的 token 字段
config.headers.token = window.sessionStorage.getItem('adminToken');
return config;
})
4. 在vue项目main.js中添加响应拦截器
// 添加响应拦截器
axios.interceptors.response.use((resp) => { //正常响应拦截
if (resp.data.code == 500) {
ElementUI.Message({message: resp.data.desc,type: "error"})
}
if (resp.data.code == 401) {
ElementUI.Message({message: resp.data.desc,type: "warning"})
router.replace("/login");
}
return resp;
});
本次的分享就到此为止了,希望我的分享能给您带来帮助,创作不易也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见
海漫浩浩,我亦苦作舟!大家一起学习,一起进步! 本人微信:g2279605572(欢迎大家与我交流)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)