Chrome扩展Manifest v3中如何获取JSONP数据?
问题:Manifest v3环境下如何正确获取JSONP数据
错误信息
拒绝加载脚本 'https://dorar.net/dorar_api.json?skey=انما الاعمال بالنياتpage=1&callback=jsonp_callback_255',因为它违反了以下内容安全策略指令:"script-src 'self'"。注意未显式设置'script-src-elem',因此使用'script-src'作为回退。
Chrome扩展Manifest v3不允许加载外部脚本,因此无法直接获取JSONP API——所有处理JSONP的库本质都是加载外部脚本。我已将fetchJsonp下载到本地文件夹,并在popup.html中引入:
<script src="./thirdparty/fetchJsonp.min.js"></script>
相关代码
background.js
chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ title: 'Check this hadith', id: 'check-hadith', contexts: ['selection'], }); }); chrome.contextMenus.onClicked.addListener(async (info, tab) => { chrome.storage.local.set({ text: info.selectionText }, async () => { await chrome.windows.create({ url: chrome.runtime.getURL('popup.html'), type: 'popup', }); }); });
popup.js
import { getAllHadith, getAllHadithInfo, } from './utils/extractHadithInfo.js'; const searchForHadithByText = async (text, page = 1) => { const url = `https://dorar.net/dorar_api.json?skey=${text}&page=${page}`; const data = await convertToJSON(url); return data; }; const convertToJSON = async (url) => { try { const data = await fetchJsonp(encodeURI(url)); // 错误发生在此处!!! // 其余代码与错误无关 const html = he.decode(data.ahadith.result); const allHadith = getAllHadith(html); const allHadithInfo = getAllHadithInfo(html); const result = allHadith.map((hadith, index) => { return { ...hadith, ...allHadithInfo[index], }; }); return result; } catch (err) { console.error(err); } }; const cards = document.getElementsByClassName('cards')[0]; chrome.storage.local.get('text', async ({ text }) => { const allHadith = await searchForHadithByText(text); const allCardsDiv = allHadith.map((_hadith) => { const { hadith, el_rawi, el_mohdith, source, number_or_page, grade } = _hadith; return `<div class="card"> <p class="hadith-text">${hadith}</p> <div class="hadith-info"> <p class="hadith-rawi"><span>الراوي:</span> ${el_rawi}</p> <p class="hadith-mohdith"><span>المتحدث:</span> ${el_mohdith}</p> <p class="hadith-source"><span>المصدر:</span> ${source}</p> <p class="hadith-number"><span>رقم الحديث أو الصفحة:</span> ${number_or_page}</p> <p class="hadith-grade"><span>صحة الحديث:</span> ${grade}</p> </div> </div>`; }); cards.innerHTML = allCardsDiv.join(''); });
manifest.json
{ "name": "Hadith Checker", "description": "Checking the selected hadith, whether it is fabricated or authentic", "version": "0.4", "manifest_version": 3, "background": { "service_worker": "background.js" }, "permissions": ["contextMenus", "storage", "unlimitedStorage"], "icons": { "16": "./icons/icons16.png", "32": "./icons/icons32.png", "48": "./icons/icons48.png", "128": "./icons/icons128.png" } }
文件夹结构
- icons + thirdparty | - fetchJsonp.min.js - he.min.js + utils | - extractHadithInfo.js - background.js - manifest.json - popup.css - popup.js - README.md
解决方案
在Manifest v3中,由于CSP限制无法直接执行外部JSONP脚本,正确做法是通过后台Service Worker发起请求并手动处理JSONP响应,具体步骤如下:
1. 更新manifest.json添加权限
添加主机权限允许访问目标API:
{ "name": "Hadith Checker", "description": "Checking the selected hadith, whether it is fabricated or authentic", "version": "0.4", "manifest_version": 3, "background": { "service_worker": "background.js" }, "permissions": ["contextMenus", "storage", "unlimitedStorage"], "host_permissions": ["https://dorar.net/*"], // 新增主机权限 "icons": { "16": "./icons/icons16.png", "32": "./icons/icons32.png", "48": "./icons/icons48.png", "128": "./icons/icons128.png" } }
2. 在background.js中添加JSONP处理逻辑
后台Service Worker不受popup的CSP限制,可以直接获取响应文本并提取JSON:
chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ title: 'Check this hadith', id: 'check-hadith', contexts: ['selection'], }); }); chrome.contextMenus.onClicked.addListener(async (info, tab) => { chrome.storage.local.set({ text: info.selectionText }, async () => { await chrome.windows.create({ url: chrome.runtime.getURL('popup.html'), type: 'popup', }); }); }); // 新增消息监听,处理popup的请求 chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'fetchJsonp') { fetchJsonpFromBackground(request.url) .then(data => sendResponse({ success: true, data })) .catch(err => sendResponse({ success: false, error: err.message })); return true; // 保持端口开放以支持异步响应 } }); // 手动解析JSONP响应 async function fetchJsonpFromBackground(url) { const jsonpUrl = `${url}&callback=jsonpCallback`; const response = await fetch(jsonpUrl); const text = await response.text(); // 去除JSONP的函数包裹,提取纯JSON字符串 const jsonStr = text.replace(/^jsonpCallback\(/, '').replace(/\)$/, ''); return JSON.parse(jsonStr); }
3. 修改popup.js,改为向后台请求数据
不再直接使用fetchJsonp,通过消息机制让后台处理请求:
import { getAllHadith, getAllHadithInfo, } from './utils/extractHadithInfo.js'; const searchForHadithByText = async (text, page = 1) => { const url = `https://dorar.net/dorar_api.json?skey=${encodeURIComponent(text)}&page=${page}`; const response = await chrome.runtime.sendMessage({ action: 'fetchJsonp', url }); if (response.success) { return convertToJSON(response.data); } else { throw new Error(response.error); } }; // 调整函数,直接处理后台返回的JSON数据 const convertToJSON = (data) => { try { const html = he.decode(data.ahadith.result); const allHadith = getAllHadith(html); const allHadithInfo = getAllHadithInfo(html); const result = allHadith.map((hadith, index) => { return { ...hadith, ...allHadithInfo[index], }; }); return result; } catch (err) { console.error(err); throw err; } }; const cards = document.getElementsByClassName('cards')[0]; chrome.storage.local.get('text', async ({ text }) => { try { const allHadith = await searchForHadithByText(text); const allCardsDiv = allHadith.map((_hadith) => { const { hadith, el_rawi, el_mohdith, source, number_or_page, grade } = _hadith; return `<div class="card"> <p class="hadith-text">${hadith}</p> <div class="hadith-info"> <p class="hadith-rawi"><span>الراوي:</span> ${el_rawi}</p> <p class="hadith-mohdith"><span>المتحدث:</span> ${el_mohdith}</p> <p class="hadith-source"><span>المصدر:</span> ${source}</p> <p class="hadith-number"><span>رقم الحديث أو الصفحة:</span> ${number_or_page}</p> <p class="hadith-grade"><span>صحة الحديث:</span> ${grade}</p> </div> </div>`; }); cards.innerHTML = allCardsDiv.join(''); } catch (err) { cards.innerHTML = `<p>获取数据失败:${err.message}</p>`; } });
4. 移除popup.html中的fetchJsonp引入(可选)
由于不再在popup中直接使用fetchJsonp,可以删除以下代码:
<!-- 移除该行 --> <script src="./thirdparty/fetchJsonp.min.js"></script>
内容的提问来源于stack exchange,提问作者Ahmed El-Tabarani
相关产品推荐
相关产品推荐

