获取并呈现GitHub Markdown无需CORS - Handstand Sam

我想在另一个网站上嵌入我的GitHub项目 (opens new window)的内容,但要达到这个目的并不直接。以下是我遇到的障碍以及我是如何克服的。

如果您只想要最终解决方案,请直接跳到文章末尾。

想法1:在iframe中渲染 #

我很想只需创建一个iframe,就能在另一个网站上展示我的GitHub项目 (opens new window)的所有内容。iframe似乎是一个完美的解决方案,但是……

<iframe src="https://github.com/handstandsam/ShoppingApp"></iframe>

(opens new window)

障碍:GitHub阻止了其内容的iframe。

想法2:获取HTML并手动渲染 #

我以为我可以从我的GitHub项目 (opens new window)的网站上爬取内容,然后在我的站点上呈现它。但是,由于CORS (opens new window),我无法从另一个Web主机中获取任意内容。

注:如果我有服务器,我可以做到这一点,因为我不会遇到CORS (opens new window)问题,但我试图在没有服务器的情况下完全在前端网页上完成这个任务。

障碍CORS (opens new window)浏览器安全策略。

想法3:使用GitHub API获取README文件 #

GitHub有一个很棒的API,我们可以用它来访问存储库的内容 (opens new window)!我无法使用它来获取整个项目页面的渲染,但我可以访问像我的README.md (opens new window)这样的单个文件。

https://api.github.com/repos/handstandsam/ShoppingApp/contents/README.md

这样我就可以下载文件的内容。问题是,由于CORS (opens new window),我无法在前端浏览器中直接执行此操作。

您感受到了一种主题吗?在浏览器中进行操作是困难的,但它有助于使我们在网络上更安全,所以我无法反驳这一点。

障碍CORS (opens new window)浏览器安全策略。

想法4:使用GitHub API进行JSONP #

JSONP (opens new window)(带填充的JSON)是CORS的一种解决方法。您基本上从第三方网站加载一个任意的JavaScript代码片段,并让它调用一个您知道名称的任意函数。

好吧,GitHub支持JSONP (opens new window)!我们将从https://api.github.com/repos/handstandsam/ShoppingApp/contents/README.md?callback=myCallback (opens new window)加载JavaScript到我们的页面中,加载完成后,它将调用myCallback(results),假设远程服务器支持JSONP。

这将使我们暴露于许多安全漏洞,所以只与受信任的网站一起使用此方法。它们可以在您的网站上的上下文中任意执行代码,而您却不知道。

注:JSONP使用加载第三方JavaScript的相同功能来执行诸如分析跟踪或使用JS库(如BootStrap JS (opens new window))进行花哨动画等操作。它只是通过编程方式创建一个<script>标签。

障碍:API的响应包含Base64编码的内容。

想法5:解码Base64文件内容并显示README #

我能够为GitHub API定义我的回调函数,然后将文本呈现到pre(预格式化文本)元素中。我通过Base64解码GitHub API响应的“content”字段来实现这一点,然后通过编程方式创建一个pre元素并设置其textContent

function myCallback(response) {
    // 解码Base64编码的内容
    let decodedContent = atob(response.data.content);

    // 创建一个“pre”HTML标记并呈现内容
    let pre = document.createElement("pre");
    pre.textContent = decodedContent;
    document.getElementsByTagName('body')[0].append(pre);
}

障碍:内容未经格式化,只是纯粹的Markdown。

想法6:使用JS库渲染Markdown #

几乎每个功能都有相应的JavaScript库。在这种情况下,我找到了markedjs/marked (opens new window)。我只需提供Markdown字符串,它就会返回渲染后的HTML。

<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
function githubMarkdownCallback(response) {
    // 解码Base64编码的内容
    let rawMarkdown = atob(response.data.content);

    // 使用markedjs库将markdown转换为html
    let markdownHtml = marked.parse(rawMarkdown)

    // 将新的div添加到html页面的body中
    let div = document.createElement("div");
    div.innerHTML = markdownHtml;
    document.getElementsByTagName('body')[0].append(div);
}

最终解决方案! #

从GitHub的公共API中获取我的项目的README.md (opens new window)内容,并使用JSONP (opens new window)markedjs (opens new window)将Markdown渲染为HTML。

<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script type="text/javascript">
function myCallback(response) {
    // 解码Base64编码的内容
    let rawMarkdown = atob(response.data.content);

    // 使用markedjs库将markdown转换为html
    let markdownHtml = marked.parse(rawMarkdown)

    // 将新的div添加到html页面的body中
    let div = document.createElement("div");
    div.innerHTML = markdownHtml;
    document.getElementsByTagName('body')[0].append(div);
}

let script = document.createElement('script');
script.src = 'https://api.github.com/repos/handstandsam/ShoppingApp/contents/README.md?callback=myCallback';
document.getElementsByTagName('head')[0].appendChild(script);
</script>