☻Blog("Laziji")

System.out.print("辣子鸡的博客");

当Web项目前后端分离开发的时候, 由于域名不一致, 会出现无法请求和无法维持会话的情况

OPTIONS

在前端Ajax请求后台的时候, 打开控制台可以看到, 每一次请求之前都会有一次OPTIONS类型的请求
OPTIONS称为预检请求, 通过这个请求, 浏览器会告知服务器,接下来的请求的情况

1
2
Access-Control-Request-Method: POST 
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

得到服务器的回应后浏览器便知道这次请求是否被允许

OPTIONS的处理

后台可以在拦截器或过滤器中处理这个请求, 这里以Springboot后台的拦截器为例

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) {
String method = request.getMethod().toLowerCase();
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Expose-Headers", "TK");
if (method.equals("options")){
return false;
}
//...
return true;
}

Access-Control-Allow-Origin 代表允许请求源, 设置为*或设置为前端的域名即可解决跨域无法请求的问题例如http://domain
Access-Control-Allow-Methods 表示允许的请求方式

以下两个特别说明
Access-Control-Allow-Headers 代表允许浏览器可以向 服务器发送哪些请求头
Access-Control-Expose-Headers 代表允许浏览器可以读取哪些服务器发送的请求头
均限制在客户端上

利用Token保持会话

传统开发前后端能维持会话, 是因为当服务器调用了HttpSession时, 会将SessionId放在请求头的set-cookie中, 浏览器读取到了这个信息, 就把SessionId保存在本地, 待下次请求时以Cookie的形式带给服务器, 服务器根据收到的SessionId找到Session 以继续会话

现在由于前后端分离后使用Cookie的种种不便, 可以换另一种方式来进行SessionId的传输, 就是把SessionId放在请求头中带给浏览器

接上一段代码

1
2
3
4
5
6
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) {
//...
HttpSession session = request.getSession();
response.setHeader("TK",session.getId());
return true;
}

由于设置了response.setHeader("Access-Control-Expose-Headers", "TK");
表示客户端可以读取header中的TK字段, 客户端就可以读取放在TK中的SessionId保存到本地, 存储方式可以是Cookie或者是LocalStorage都可以
当需要请求时用http://service;jsessionid=${token}的形式传给服务器, 这个与传统Web中Cookie:JSESSIONID=sessionid的形式效果是一致的
如此就达到了保持会话的目的了

为什么能维持会话

服务器根据sessionid保持会话这件事是容器完成的, 根据的就是请求中所传来的URL后的;jsessionid= 或者 cookie中的JSESSIONID=,
容器根据sessionid自动找到内存中的Session。
这也是选择;jsessionid=方式的原因。这会让你没有感觉像在做前后端分离的开发, 就像传统的JSP开发一样, 前后端融合在一起。

当然你也可以用其他方式手动操作, 比如直接传参 http://service?JID=${token}, 在请求头中带过去之类的, 然后手动取得ID再去找对应的Session, 不过这就有点多此一举了

现在有个需求, 后端对于每一张数据库的表都暴露三个主要APInamespace/listnamespace/savenamespace/update 以及其他扩展接口。
现在需要用JavaScript编写一种较通用可扩展的代码。

当然在JavaScript中实现这个需求方法很多, 这里讲述的是如何在ECMAScript 6中用类似Java中的泛型, 继承来解决

最后使用如下

1
2
3
import AppApi from "./AppApi"

AppApi.list()

AppApi.js

Api类如下 十分的OO

1
2
3
4
5
import BaseApi from "./BaseDBApi"

export default class AppApi extends BaseApi("/app") {
//...extension method
}

BaseApi.js

由于JS动态的特性, 可以比Java更加灵活 动态构造类, static 标注的方法可以直接通过类名, 子类继承后 也可以通过子类的类名调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import request from "@/common/request"

export default function builder(baseUrl) {
return class BaseApi {
static list(form) {
return request({
url: `${baseUrl}/list`,
data: form
})
}

static save(form) {
return request({
url: `${baseUrl}/save`,
data: form
})
}

static update(form) {
return request({
url: `${baseUrl}/update`,
data: form
})
}
}
}
0%