拦截器的错误配置导致前端正常请求被拦截重定向

写在前面

原本的标题是“HTTP访问控制(CROS) 初识”,但是我发现写的内容其实就是mozilla开发文档里关于CROS里的内容。所以我想我应该记下自己为什么去了解关于CROS的内容。

起因

前端写的一个普通的Post请求,他和我说请求返回302了。我感到很奇怪,我自己用Postman调试都已经通过了,应该是没有问题的。随后前端和我说访问该模块的所有类型 content typeapplication/json 的Post请求都会被302重定向。我怀疑可能是被拦截器拦截了。

在拦截器里打印了下log,发现确实是被拦截器拦截了,原因是请求没有Session信息,而且这些请求都是OPTIONS的方法。就去了解了到了这是和HTTP里CROS相关的内容。

HTTP请求的OPTIONS方法

在HTTP中所有的复杂请求,游览器都会发送一个预检请求到服务器,以获知服务器是否允许该实际请求(Postman中并不会发送预检请求所以没有被服务器拦截)。所以就算前端没有存在跨域,但是请求还是会被后台拦截并被重定向了。

解决方法

在拦截器中对拦截的方法进行判断,对于一些不需要进行拦截的请求直接返回true。


CROS是什么

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

什么时候会进行CROS

  • 由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求
  • Web 字体 (CSS 中通过 @font-face 使用跨域字体资源)
  • WebGL 贴图
  • 使用 drawImage 将 Images/video 画面绘制到 canvas
  • 样式表(使用 CSSOM)

XHR ( XMLHttpRequest ) 是一个对象,可以和服务器交互。在 Ajax 中被大量使用。Fetch是ES6中的一个新内容。

哪些请求需要发送预检请求

当请求满足下述任一条件时,即应首先发送预检请求:

  • 使用了下面任一 HTTP 方法:
    • PUT
    • DELETE
    • CONNECT
    • OPTIONS
    • TRACE
    • PATCH
  • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意额外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 的值不属于下列之一:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
  • 请求中使用了ReadableStream对象。

LeetCode-NO.15 三数之和

三数之和

第一次解题

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
27
28
29
30
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums.length < 3) {
return res;
}
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length-1; j++) {
int sub = Arrays.binarySearch(Arrays.copyOfRange(nums, j+1, nums.length), -(nums[i] + nums[j]));
if (sub >= 0) {
List<Integer> ints = new ArrayList<>();
ints.add(nums[i]);
ints.add(nums[j]);
ints.add(nums[sub + j+1]);
ints.sort((o1, o2) -> {
if (o1 > o2) {
return 1;
} else if (o1 == o2)
return 0;
return -1;
});
if (!res.contains(ints))
res.add(ints);
}
}
}
return res;
}
}

先排序使得Arrays.binarySearch方法可以正确的返回值。

主要思想是先确定两个值,然后求出这两个值的和。如果第三个值等于和的反数,则第三个值就是需要的。

再通过给找到的集合排序,匹配需要返回的集合里有没有相同的值来去重。不过这种方法太慢了,提交显示超时。

第二次解题

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
27
28
29
30
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums.length < 3) {
return res;
}
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
int[] jints = new int[nums.length];
if (i > 0 && nums[i - 1] == nums[i]) continue;
for (int j = i + 1; j < nums.length; j++) {
for (int k = j - 1; k > i; k--) {
if (jints[k] == nums[j]) {
if (j > i + 1 && k > i && k <= j) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[k]);
temp.add(nums[j]);
if (!res.contains(temp))
res.add(temp);
break;
}
}
}
jints[j] = -nums[i] - nums[j];
}
}
return res;
}
}

仅仅是把Arrays.binarySearch换成自己实现而已,基本没做什么改进,提交还是显示超时。

第三次解题

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
27
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums.length < 3) {
return res;
}
Arrays.sort(nums);
Map<Integer, Integer> map = new HashMap<>(nums.length);
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i - 1] == nums[i]) continue;
for (int j = i + 1; j < nums.length; j++) {
if (j != i + 1 && nums[j - 1] == nums[j]) continue;
int sum = -nums[i] - nums[j];
Integer index = map.get(sum);
if (index != null) {
if (index > j) {
res.add(Arrays.asList(nums[i], nums[j], sum));
}
}
}
}
return res;
}
}

不再记录每一次循环后需要的值,而是先把需要的值放在Map集合中,经过两次循环后查询需要的值是不是在Map集合中。

因为Map集合中已经存在的key是无法放入的,所以最终找出来的结果不会有两个相同的值。同时跳过前两个值相同的元素,因为最开始经过sort排序,所以两次for循环的查找中,我们期望的结果肯定是在j后面的。

虽然已经可以通过提交,不过这样子还是太慢了。

其他题解

双指针

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
27
28
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
List<List<Integer>> res = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = n - 1;
while (left < right) {
int tmp = nums[i] + nums[left] + nums[right];
if (tmp == 0) {
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) left += 1;
while (left < right && nums[right] == nums[right - 1]) right -= 1;
left += 1;
right -= 1;
} else if (tmp > 0) right -= 1;
else left += 1;
}
}
return res;
}
}
// 作者:powcai
// 链接:https://leetcode-cn.com/problems/two-sum/solution/shuang-zhi-zhen-by-powcai-2/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下标计数

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class Solution {
public List<List<Integer>> threeSum(int[] nums){
if (nums.length < 3)
return Collections.emptyList();
List<List<Integer>> res = new ArrayList<>();
int minValue = Integer.MAX_VALUE;
int maxValue = Integer.MIN_VALUE;
int negSize = 0;
int posSize = 0;
int zeroSize = 0;
/* 遍历数组,记录数组中的正数负数和零的个数,同时记录最小值和最大值 */
for (int v : nums) {
if (v < minValue)
minValue = v;
if (v > maxValue)
maxValue = v;
if (v > 0)
posSize++;
else if (v < 0)
negSize++;
else
zeroSize++;
}
/* 如果数组中0的个数大于等于3个,则给返回中添加(0,0,0)的结果 */
if (zeroSize >= 3)
res.add(Arrays.asList(0, 0, 0));
/* 如果只有正数或者负数,就直接返回结果 */
if (negSize == 0 || posSize == 0)
return res;
/* 如果最大值大于最小值的两倍,那最大值等于两倍最小数的反数。原因是这样的最大值没有匹配的两个数是它们的和等于0 */
if (minValue * 2 + maxValue > 0)
maxValue = -minValue * 2;
/* 最小值同理 */
else if (maxValue * 2 + minValue < 0)
minValue = -maxValue * 2;
int[] map = new int[maxValue - minValue + 1];
int[] negs = new int[negSize];
int[] poses = new int[posSize];
negSize = 0;
posSize = 0;
for (int v : nums) {
if (v >= minValue && v <= maxValue) {
/* 判断v这个值有没有重复,如果重复则记录相同值的个数。map[i]的初始值是0,执行map[i]++的操作后会值增加1 */
if (map[v - minValue]++ == 0) {
if (v > 0)
poses[posSize++] = v;
else if (v < 0)
negs[negSize++] = v;
}
}
}
Arrays.sort(poses, 0, posSize);
Arrays.sort(negs, 0, negSize);
int basej = 0;
for (int i = negSize - 1; i >= 0; i--) {
int nv = negs[i];
/* minp是当前负数的反数的一半,向下取整 */
int minp = (-nv) >>> 1;
while (basej < posSize && poses[basej] < minp)
basej++;
for (int j = basej; j < posSize; j++) {
int pv = poses[j];
int cv = 0 - nv - pv;
/* 如果cv比当前可以使用的最大值小并且比可使用的最小值大,就可以去判断 */
if (cv >= nv && cv <= pv) {
/* 如果cv和nv的值相等,则查询一下是否有相同的值 */
if (cv == nv) {
if (map[nv - minValue] > 1)
res.add(Arrays.asList(nv, nv, pv));
} else if (cv == pv) { //同理
if (map[pv - minValue] > 1)
res.add(Arrays.asList(nv, pv, pv));
} else {
/* 如果数组中有这个值,则返回结果 */
if (map[cv - minValue] > 0)
res.add(Arrays.asList(nv, cv, pv));
}
} else if (cv < nv)
break;
}
}
return res;
}
}
// 来源:力扣(LeetCode) 提交记录 -> 执行用时为 30 ms 的范例

注解是我自己添加的,可能有理解不对的地方。

看了这些解法,自己总结了一些经验:

查找数组中的数,经过处理,以数的值为下标,查这个下标在新数组中的值是否大于0来判断,效率会高很多,减少了很多遍历的操作。

提高效率,需要尽可能的减少重复的遍历操作,在找到需求之后尽量的跳出循环。就算在之前可能需要提前遍历一下内容,总体而言也是提高了效率。