构建 Node.js Express API 以将 Markdown 转换为 HTML
已发表: 2022-03-10Markdown 是一种轻量级的文本标记语言,允许将标记的文本转换为各种格式。 创建 Markdown 的最初目标是让人们“使用易于阅读和易于编写的纯文本格式进行编写”,并可选择将其转换为结构上有效的 XHTML(或 HTML)。 目前,随着 WordPress 支持 Markdown,该格式已得到更广泛的使用。
撰写本文的目的是向您展示如何使用 Node.js 和 Express 框架来创建 API 端点。 我们将通过构建一个将 Markdown 语法转换为 HTML 的应用程序来学习这一点。 我们还将向 API 添加身份验证机制,以防止滥用我们的应用程序。
一个 Markdown Node.js 应用程序
我们称为“Markdown 转换器”的小型应用程序将使我们能够发布 Markdown 样式的文本并检索 HTML 版本。 该应用程序将使用 Node.js Express 框架创建,并支持转换请求的身份验证。
我们将分小阶段构建应用程序——最初使用 Express 创建一个脚手架,然后随着我们的进展添加各种功能,例如身份验证。 因此,让我们通过创建脚手架从构建应用程序的初始阶段开始。
第 1 阶段:安装 Express
假设您已经在系统上安装了 Node.js,创建一个目录来保存您的应用程序(我们称之为“ markdown-api
”),然后切换到该目录:
$ mkdir markdown-api $ cd markdown-api
使用 npm init 命令为您的应用程序创建一个package.json文件。 此命令会提示您输入许多信息,例如应用程序的名称和版本。
现在,只需按 Enter即可接受其中大多数的默认值。 我已将默认入口点文件用作index.js ,但您可以根据自己的喜好尝试app.js或其他文件。
现在在markdown-api
目录中安装 Express 并将其保存在依赖项列表中:
$ npm install express --save
在当前目录( markdown-api
)创建一个index.js文件,并添加以下代码来测试 Express 框架是否安装正确:
Const express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('Hello World!'); }); app.listen(3000);
现在浏览到 URL https://localhost:3000
以检查测试文件是否正常工作。 如果一切正常,我们将看到一个 Hello World! 在浏览器中打招呼,我们可以继续构建一个基础 API 来将 Markdown 转换为 HTML。
第 2 阶段:构建基础 API
我们 API 的主要目的是将 Markdown 语法中的文本转换为 HTML。 API 将有两个端点:
-
/login
-
/convert
login
端点将允许应用程序验证有效请求,而convert
端点将(显然)将 Markdown 转换为 HTML。
下面是调用这两个端点的基本 API 代码。 login
调用只返回一个“Authenticated”字符串,而convert
调用返回你提交给应用程序的任何 Markdown 内容。 home 方法只返回一个“Hello World!” 细绳。
const express = require("express"); const bodyParser = require('body-parser'); var app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.get('/', function(req, res){ res.send('Hello World!'); }); app.post('/login', function(req, res) { res.send("Authenticated"); }, ); app.post("/convert", function(req, res, next) { console.log(req.body); if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { res.json(["markdown", req.body.content]); } }); app.listen(3000, function() { console.log("Server running on port 3000"); });
我们使用body-parser
中间件来轻松解析应用程序的传入请求。 中间件将使您可以在req.body
属性下使用所有传入的请求。 您可以不使用额外的中间件,但添加它可以更轻松地解析各种传入的请求参数。
您可以通过简单地使用 npm 来安装body-parser
:
$ npm install body-parser
现在我们已经有了我们的虚拟存根函数,我们将使用 Postman 来测试它。 让我们首先从 Postman 的简要概述开始。
邮递员概述
Postman 是一种 API 开发工具,可以轻松地在浏览器中或通过下载桌面应用程序(浏览器版本现已弃用)来构建、修改和测试 API 端点。 它能够发出各种类型的 HTTP 请求,即 GET、POST、PUT、PATCH。 它适用于 Windows、macOS 和 Linux。
下面是 Postman 的界面体验:

要查询 API 端点,您需要执行以下步骤:
- 在顶部的网址栏中输入您要查询的网址;
- 选择 URL 栏左侧的 HTTP 方法发送请求;
- 单击“发送”按钮。
Postman 会将请求发送到应用程序,检索任何响应并将其显示在下部窗口中。 这是如何使用 Postman 工具的基本机制。 在我们的应用程序中,我们还必须向请求中添加其他参数,这将在以下部分中进行介绍。
使用邮递员
现在我们已经看到了 Postman 的概述,让我们继续在我们的应用程序中使用它。
从命令行启动你的markdown-api
应用程序:
$ node index.js
为了测试基本 API 代码,我们从 Postman 对应用程序进行 API 调用。 请注意,我们使用 POST 方法将要转换的文本传递给应用程序。
应用程序目前接受 Markdown 内容通过content
POST 参数进行转换。 我们将其作为 URL 编码格式传递。 目前,该应用程序以 JSON 格式逐字返回字符串——第一个字段始终返回字符串markdown
,第二个字段返回转换后的文本。 稍后,当我们添加 Markdown 处理代码时,它会返回转换后的文本。
第三阶段:添加 Markdown 转换器
现在构建了应用程序脚手架,我们可以查看Showdown
JavaScript 库,我们将使用它来将 Markdown 转换为 HTML。 Showdown 是一个用 JavaScript 编写的双向 Markdown 到 HTML 转换器,它允许您将 Markdown 转换为 HTML 并返回。

使用 npm 安装包:
$ npm install showdown
将所需的摊牌代码添加到脚手架后,我们得到以下结果:
const express = require("express"); const bodyParser = require('body-parser'); const showdown = require('showdown'); var app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); converter = new showdown.Converter(); app.get('/', function(req, res){ res.send('Hello World!'); }); app.post('/login', function(req, res) { res.send("Authenticated"); }, ); app.post("/convert", function(req, res, next) { if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); } }); app.listen(3000, function() { console.log("Server running on port 3000"); });
主转换器代码位于/convert
端点中,如下所示。 这会将您发布的任何 Markdown 文本转换为 HTML 版本,并将其作为 JSON 文档返回。
... } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); }
进行转换的方法是converter.makeHtml(text)
。 我们可以使用setOption
方法为 Markdown 转换设置各种选项,格式如下:
converter.setOption('optionKey', 'value');
因此,例如,我们可以设置一个选项来自动插入和链接指定的 URL,而无需任何标记。
converter.setOption('simplifiedAutoLink', 'true');
与 Postman 示例一样,如果我们向应用程序传递一个简单的字符串(例如Google home https://www.google.com/
),如果启用了simplifiedAutoLink
自动链接,它将返回以下字符串:
<p>Google home <a href="https://www.google.com/">https://www.google.com/</a></p>
如果没有该选项,我们将不得不添加标记信息以实现相同的结果:

Google home <https://www.google.com/>
有许多选项可以修改 Markdown 的处理方式。 完整列表可在 Showdown 网站上找到。
所以现在我们有了一个带有单个端点的 Markdown-to-HTML 转换器。 让我们进一步并添加身份验证以具有应用程序。
第 4 阶段:使用 Passport 添加 API 身份验证
在没有适当身份验证的情况下向外界公开您的应用程序 API 将鼓励用户无限制地查询您的 API 端点。 这将邀请不道德的元素滥用您的 API,并且还会给您的服务器带来未经审核的请求。 为了缓解这种情况,我们必须添加适当的身份验证机制。
我们将使用 Passport 包向我们的应用程序添加身份验证。 就像我们之前遇到的body-parser
中间件一样,Passport 是 Node.js 的身份验证中间件。 我们将使用 Passport 的原因是它有多种身份验证机制可供使用(用户名和密码、Facebook、Twitter 等),这使用户可以灵活地选择特定机制。 Passport 中间件可以轻松地放入任何 Express 应用程序中,而无需更改太多代码。
使用 npm 安装包。
$ npm install passport
我们还将使用local
策略(稍后将解释)进行身份验证。 所以也安装它。
$ npm install passport-local
您还需要为 Passport 使用的 Node.js 添加 JWT(JSON Web Token) 编码和解码模块:
$ npm install jwt-simple
护照策略
Passport 使用策略的概念来验证请求。 策略是多种方法,可让您对请求进行身份验证,包括验证用户名和密码凭据、使用 OAuth(Facebook 或 Twitter)进行身份验证或使用 OpenID 等简单情况。 在对请求进行身份验证之前,必须配置应用程序使用的策略。
在我们的应用程序中,我们将使用简单的用户名和密码验证方案,因为它易于理解和编码。 目前,Passport 支持 300 多种策略,可在此处找到。
虽然 Passport 的设计看似复杂,但代码中的实现却非常简单。 这是一个示例,展示了我们的/convert
端点是如何进行身份验证的。 正如您将看到的,向方法添加身份验证非常简单。
app.post("/convert", passport.authenticate('local',{ session: false, failWithError: true }), function(req, res, next) { // If this function gets called, authentication was successful. // Also check if no content is sent if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); }}, // Return a 'Unauthorized' message back if authentication failed. function(err, req, res, next) { return res.status(401).send({ success: false, message: err }) });
现在,连同要转换的 Markdown 字符串,我们还必须发送用户名和密码。 这将使用我们的应用程序用户名和密码进行检查并进行验证。 由于我们使用本地策略进行身份验证,因此凭据存储在代码本身中。
尽管这听起来像是一场安全噩梦,但对于演示应用程序来说,这已经足够好了。 这也使我们更容易理解我们示例中的身份验证过程。 顺便说一句,一种常用的安全方法是将凭据存储在环境变量中。 尽管如此,很多人可能不同意这种方法,但我觉得这种方法相对安全。
带有身份验证的完整示例如下所示。
const express = require("express"); const showdown = require('showdown'); const bodyParser = require('body-parser'); const passport = require('passport'); const jwt = require('jwt-simple'); const LocalStrategy = require('passport-local').Strategy; var app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); converter = new showdown.Converter(); const ADMIN = 'admin'; const ADMIN_PASSWORD = 'smagazine'; const SECRET = 'secret#4456'; passport.use(new LocalStrategy(function(username, password, done) { if (username === ADMIN && password === ADMIN_PASSWORD) { done(null, jwt.encode({ username }, SECRET)); return; } done(null, false); })); app.get('/', function(req, res){ res.send('Hello World!'); }); app.post('/login', passport.authenticate('local',{ session: false }), function(req, res) { // If this function gets called, authentication was successful. // Send a 'Authenticated' string back. res.send("Authenticated"); }); app.post("/convert", passport.authenticate('local',{ session: false, failWithError: true }), function(req, res, next) { // If this function gets called, authentication was successful. // Also check if no content is sent if(typeof req.body.content == 'undefined' || req.body.content == null) { res.json(["error", "No data found"]); } else { text = req.body.content; html = converter.makeHtml(text); res.json(["markdown", html]); }}, // Return a 'Unauthorized' message back if authentication failed. function(err, req, res, next) { return res.status(401).send({ success: false, message: err }) }); app.listen(3000, function() { console.log("Server running on port 3000"); });
显示添加了身份验证的转换的 Postman 会话如下所示。

在这里,我们可以看到我们已经从 Markdown 语法中获得了正确的 HTML 转换字符串。 虽然我们只要求转换一行 Markdown,但是 API 可以转换大量的文本。
这结束了我们使用 Node.js 和 Express 构建 API 端点的简短尝试。 API 构建是一个复杂的主题,在构建 API 时应该注意一些更细微的细微差别,遗憾的是我们没有时间在这里讨论,但可能会在以后的文章中介绍。
从另一个应用程序访问我们的 API
现在我们已经构建了一个 API,我们可以创建一个小的 Node.js 脚本来向您展示如何访问该 API。 对于我们的示例,我们需要安装request
npm 包,它提供了一种简单的方式来发出 HTTP 请求。 (您很可能已经安装了这个。)
$ npm install request --save
下面给出了向我们的 API 发送请求并获取响应的示例代码。 如您所见, request
包大大简化了问题。 要转换的降价在textToConvert
变量中。
在运行以下脚本之前,请确保我们之前创建的 API 应用程序已经在运行。 在另一个命令窗口中运行以下脚本。
注意:我们使用(back-tick)
符号来跨越textToConvert
变量的多个 JavaScript 行。 这不是单引号。
var Request = require("request"); // Start of markdown var textToConvert = `Heading ======= ## Sub-heading Paragraphs are separated by a blank line. Two spaces at the end of a line produces a line break. Text attributes _italic_, **bold**, 'monospace'. A [link](https://example.com). Horizontal rule:`; // End of markdown Request.post({ "headers": { "content-type": "application/json" }, "url": "https://localhost:3000/convert", "body": JSON.stringify({ "content": textToConvert, "username": "admin", "password": "smagazine" }) }, function(error, response, body){ // If we got any connection error, bail out. if(error) { return console.log(error); } // Else display the converted text console.dir(JSON.parse(body)); });
当我们向 API 发出 POST 请求时,我们会提供要转换的 Markdown 文本以及凭证。 如果我们提供了错误的凭据,我们将收到一条错误消息。
{ success: false, message: { name: 'AuthenticationError', message: 'Unauthorized', status: 401 } }
对于正确授权的请求,上述示例 Markdown 将转换为以下内容:
[ 'markdown', `<h1>Heading</h1> <h2>Sub-heading</h2> <p>Paragraphs are separated by a blank line.</p> <p>Two spaces at the end of a line<br /> produces a line break.</p> <p>Text attributes <em>italic</em>, <strong>bold</strong>, 'monospace'. A <a href="https://example.com">link</a>. Horizontal rule:</p>` ]
尽管我们在这里对 Markdown 进行了硬编码,但文本可以来自各种其他来源——文件、Web 表单等。 请求过程保持不变。
请注意,由于我们将请求作为application/json
内容类型发送; 我们需要使用 json 对主体进行编码,因此需要调用JSON.stringify
函数。 如您所见,测试或 API 应用程序需要一个非常小的示例。
结论
在本文中,我们开始了一个教程,目的是学习如何使用 Node、js 和 Express 框架来构建 API 端点。 我们决定创建一个将 Markdown 语法转换为 HTML 的 API,而不是毫无目的地构建一些虚拟应用程序,从而在有用的上下文中锚定或学习。 在此过程中,我们向 API 端点添加了身份验证,并且我们还看到了使用 Postman 测试应用程序端点的方法。