在一些特殊场景中,我们在客户端想要去获取服务端接口设置的一些自定义响应头,服务端该如何处理,客户端才能取到这些自定义响应头的值呢?
特殊场景,我这里也举例一下,原生页面webView嵌入web页面。这个时候如果在app内,用户已经登录了,就需要h5页面能够拿到用户信息,但是登录信息在app中,所以需要原生把这个信息传递给h5页面。
这个时候会有两种方法,一个是通过接口请求的方式,我们指定尽管是一个页面,在浏览器打开,但其实本质是一个get请求。所以能够通过这个接口传递数据;第二个方法就是与h5页面进行通信。
通过接口请求,这里也有讲究,直接把数据传递在url里面,这样h5页面是肯定能拿到这个数据的,但是有个问题,就是把所有信息暴露出来,只有能够拿到链接,用户信息就全部暴露了;讲到这里应该很多人想到了使用数据加密,确实加密可以防御,但并不是最好的选择(尽管这个值被加密了,url上也仍然不理想,因为url有长度限制),因为加密肯定要能够解密,不然h5页面怎么取到数据,这个时候,仍然有暴露的风险;第二种方式就是将信息传递在接口响应头里,这样除非使用抓包,不然这个信息是拿不到,也显示不出来的。
所以通常在链接上不会随意携带敏感数据,但是技术方案时,就必须要区分,这个时候,大家都会想在响应头里面存放自定义数据,因为这样就只有能把这个接口抓包获取才知道的信息,普通用户无感知。
但是普通在服务端响应头增加自定义字段,在客户端直接读取是不行的,比如下面例子
客户端直接去读取响应头(response headers)
服务端代码:
app.all("*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Authorization', '1234');
next();
})
app.get('/list', (req, res) => {
res.send({
list: [1,2,3,4],
code: 200
})
})
客户端代码:
const testHeader = () => {
fetch('http://localhost:80/').then(res => {
console.log(res.headers.get('Authorization')) // 读取请求头字段
return res.json()
}).then(res => {
console.log(res)
})
}
testHeader()
我们能够在控制台中看到这个属性,但是这个请求头的值,读取是null
所以这个时候,我们陷入沉思,请求接口时,我们知道能够在客户端定义请求头,但是在客户端去读取响应头字段,我们还真不会了
后端读取客户端请求头(request headers)
按理说客户端请求接口时,只有后端指定了有这个自定义请求头,前端就能在发送请求时,添加对应的自定义请求头字段
例如下面添加自定义请求头trackId
服务器端代码
app.all("*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Headers', 'trackId');
next();
})
客户端代码
const testHeader = () => {
fetch('http://localhost:80/list', {
headers: {
'trackId': '200000001'
},
}).then(res => {
console.log(res.headers.get('Authorization'))
return res.json()
}).then(res => {
console.log(res)
})
}
testHeader()
服务端接受前端返回的请求头
app.get('/list', function (req, res) {
console.log(req.headers)
res.send({
list: [1,2,3,4],
code: 200
})
})
这其实就是在后端接受请求头参数了,通常企业中都是这样来发送一些用户token,确保用户此刻是登录状态,通常也有来区分是哪个用户请求了这个接口
前端就自动去添加这个自定义请求头的时候,控制台会直接报错,请求会被浏览器拦截。请求不会发送到后端
可以直接看网络请求,请求被拦截了
谷歌浏览器控制台说明:https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#provisional-headers
询问一下chatGPT如何解决这个错误
就会发现需要后端设置这个自定义请求头字段
客户端正确读取服务端设置的响应头(response headers)
从上面的例子,我们发现前端不能直接设置自定义请求头,需要与后端接口协议好,这个接口需要传递哪些请求头字段,如果后端没有设置这个res.header(‘Access-Control-Allow-Headers’, ‘trackId’);
所以前端接受后端自定义的响应头字段,读取不到,问题出在后端
后端服务需要设置将自定义响应头字段暴露给客户端访问
响应标头 Access-Control-Expose-Headers
服务端代码
app.all("*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Headers', 'trackId');
res.header('Access-Control-Expose-Headers', '*, Authorization'); // 允许暴露响应头数据给客户端
res.header('Authorization', '1234');
next();
})
app.get('/list', function (req, res) {
console.log(req.headers)
res.send({
list: [1,2,3,4],
code: 200
})
})
客户端获取响应头字段
const testHeader = () => {
fetch('http://localhost:80/list', {
headers: {
'trackId': '200000001'
},
}).then(res => {
console.log(res.headers.get('Authorization'))
return res.json()
}).then(res => {
console.log(res)
})
}
testHeader()
好了,今天的分享就到这里结束了,如果你有任何疑惑,欢迎在评论区讨论,或者你有更好的方案,也可以在评论区分享一下~