img标签请求 添加自定义header(一)

需求场景:上传图片后接口返回文件id,展示图片时链接地址为/file/preview?id=xxx。但系统中的接口都需要鉴权(即在请求header中添加authCode),而img标签的请求是浏览器的默认行为,并不受我们的接口拦截(如axios请求拦截)约束。

通常的解决方式:

手动发请求+处理流

Vue为例,我们可以封装一个自定义图片组件authImg

<template>
    <img ref="img">
</template>
<script>
export default {
    props:{
        src:{
            type: String,
            default:''
        }
    },
    watch:{
        src:{
            handler(n){
                if(n){
                    this.getImage(n);
                }
            },
            immediate:true
        }
    },
    methods:{
        getImage(url){
            let that = this;
            that.$nextTick(()=>{
                let request = new XMLHttpRequest();
                request.responseType = 'blob';
                request.open('get', url, true);
                // 添加自定义请求头
                request.setRequestHeader('authToken', localStorage.getItem('authToken')); 
                request.onreadystatechange = e => {
                    if (request.readyState == XMLHttpRequest.DONE && request.status == 200) { 
                        that.$refs.img.src = URL.createObjectURL(request.response);
                        that.$refs.img.onload = () => {
                            that.$refs.img && URL.revokeObjectURL(that.$refs.img.src);
                        } 
                    }
                };
                request.send(null);
            });
        }
    }
}
</script>

使用:

<template>
	<authImg src="/file/preview?id=xxx"></authImg>
</template>

此方式可以处理绝大多数场景,但解决不了类似富文本中的img问题。一般富文本会保存成字符串,数据回显时,很难处理其中的img标签

通过自定义元素加载

这种方式就是扩展原有的HTML Element,实现一些自定义的功能,示例如下

// 扩展原生Image
let requestImage = function (url, element) {
    let request = new XMLHttpRequest();
    request.responseType = 'blob';
    request.open('get', url, true);
    request.setRequestHeader('authToken', localStorage.getItem('authToken')); 
    request.onreadystatechange = e => {
        if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
            element.src = URL.createObjectURL(request.response);
            element.onload = () => {
                element && URL.revokeObjectURL(element.src);
            }
        }
    };
    request.send(null);
}

class AuthImg extends HTMLImageElement {
    constructor() {
        super();
        this._lastUrl = '';
    }
    static get observedAttributes() {
        return ['authsrc'];
    }
    connectedCallback() {
        let url = this.getAttribute('authsrc');
        if (url !== this._lastUrl) {
            this._lastUrl = url;
            requestImage(url, this);
        }
        console.log('connectedCallback() is called.');
    }
}
window.customElements.define('auth-img', AuthImg, {extends: 'img'});

使用:

原始(报错401):<img src="/file/preview?id=xxx">
替换后:<img is="auth-img" authsrc="/file/preview?id=xxx"/>

此方式可以解决上述的富文本中的图片问题,以wangEdit为例

// 自定义上传图片
this.editor.customConfig.uploadImgHooks = {
    customInsert: function (insertImg, result, editor) {
        let imgUrl = result.data[0];
        that.editor.cmd.do('insertHTML', `<img is="auth-img" authsrc="${imgUrl}" alt="图片" /> `);
    }
}
serviceWorker

我们真的没办法拦截到浏览器原生的img请求吗?其实是可以的。serviceWorker具体使用可自行查找资料,这里我们简单做个示例:

<!--index.html-->
<script>
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('./sw.js');
}
</script>
//sw.js
self.addEventListener('fetch', function (evt) {
    // 拦截指定请求前缀
    if (evt.request.url.indexOf('image/view?id')>-1) {
        // 拷贝原始请求
        const request = evt.request.clone();
        // 创建headers
        const headers = new Headers();
        // 添加自定义请求头
        headers.append('authToken', '7c5c90e6-bf49-47c0-bda1-bc93255364d8'); 
        // 创建新请求
        const newRequest = new Request(request,{
            headers,
            mode: 'cors'
        })
        // 返回=>走浏览器原有逻辑
        evt.respondWith(fetch(newRequest))
    }
});

这样我们就不需要修改img标签,或者做其它处理。通过serviceWorker自动拦截对应请求,添加请求头,剩下的交给浏览器就可以了。

后记

第一、二种方式原则上差不多,第二种覆盖场景会多一些,但改动量都比较大。

第三种方式改动较小,基本上是全场景覆盖,如富文本,如css背景图设置成接口地址;但自定义性较差,比如异常回调,加loading之类的操作。

当然使用serviceWorker,同样可以对以window.open或者window.location.href形式的下载链接拦截,添加自定义请求头,就不需要前端通过调用接口获取文件流再手动转换了。

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值