Firebase云函数调用时因目标URL协议错误引发CORS问题的捕获与处理求助
我最近碰到了一个棘手的问题:当用户传入的目标URL本该使用http协议却误写成https时,我的Firebase云函数在调用fetch、extract(来自@extractus/oembed-extractor)和getLinkPreview(来自link-preview-js)这几个方法时,会触发CORS错误。更麻烦的是,这个错误既没在服务端的try/catch里被捕获,客户端的异常处理也抓不到它。
我的Firebase云函数代码(服务端)
export const getPreviewData = onCall(async (request) => { if (!request.auth) return { sucess:false, message:'request not authorized' } const { url } = request.data; let err, status; try { const response = await fetch(url); status = await response.status; } catch (e) { const error:Error = e as Error err = error.message; } ... const oEmbedData = extract(url) ... const OGData = getLinkPreview(url) ... }
客户端控制台的CORS错误信息
Access to fetch at 'https://us-central1-workboxes-dev.cloudfunctions.net/getPreviewData' from origin 'https://workboxes-dev.web.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
客户端调用云函数的代码
const functions = getFunctions() const fetchPreviewData = httpsCallable(functions, 'getPreviewData') try { const previewData = await fetchPreviewData({url}) console.log('previewData', previewData) } catch(error) { console.log('error fetching weblink data',error.message) }
正常情况下,只要URL协议正确,整个流程都能顺畅运行。但这是面向用户的界面,我必须能捕获到这种协议错误,这样才能自动把错误的https替换成正确的http来解决问题。
我最终的解决方法
后来我想到,先用fetch对传入的URL做一层校验过滤,把有协议问题的URL处理好之后,再安全地调用另外两个方法。具体代码如下:
const { url, eooptions, ogoptions } = request.data; let err = 'no error', response, ok = false, status, selectedURL = url, modifiedprotocol // 先校验传入的URL try { response = await fetch(selectedURL); status = response.status; ok = response.ok } catch (e) { const error:Error = e as Error err = error.message; const urlData = new URL(selectedURL); if (urlData.protocol == 'https:') { err = 'no error' selectedURL = 'http://' + urlData.hostname + urlData.pathname modifiedprotocol = 'http:' try { response = await fetch(selectedURL); status = response.status; ok = response.ok; } catch (e) { const error:Error = e as Error err = error.message; } } } if (!ok) { return { status, ok, err, selectedURL } } // 现在可以安全地调用extract和getLinkPreview了 ...
备注:内容来源于stack exchange,提问作者HenrikBechmann

