Express.js https://www.e-learn.cn/tag/expressjs zh-hans 构建RESTful API的13种最佳实践 https://www.e-learn.cn/topic/3975110 <span>构建RESTful API的13种最佳实践</span> <span><span lang="" about="/user/114" typeof="schema:Person" property="schema:name" datatype="">﹥>﹥吖頭↗</span></span> <span>2020-12-23 19:45:27</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <p>翻译:Eolinker 来源:<a href="https://datayi.cn/w/3oLWMnaP" rel="nofollow">www.eolinker.com</a> Facebook、GitHub、Google以及其他许多巨头都需要一种服务和消费数据的方式。在当今的开发环境中,RESTful API仍然是服务和消费数据的最佳选择之一。 但是你是否考虑过学习行业标准?设计RESTful API的最佳实践是什么?从理论上讲,任何人都可以在不到五分钟的时间内快速启动数据API——无论是Node.js,Golang还是Python。 我们将探讨在构建RESTful API时应考虑的13种最佳实践。</p> <p>什么是RESTful API? RESTful API需要满足以下约束才能被称为RESTful API。</p> <ol><li>客户端-服务器模型:RESTful API遵循客户端-服务器模型,其中服务器为数据提供服务,而客户端连接到服务器以使用数据。客户端和服务器之间的交互是通过HTTP(S)请求进行的,该请求传输了请求的数据。</li> <li>无状态:更重要的是,RESTful API应该是无状态的。每个请求都被视为独立请求。服务器不应跟踪可能影响将来请求结果的任何内部状态。</li> <li>统一接口:最后,一致性定义了客户端和服务器之间的交互方式。RESTful API定义了命名资源的最佳实践,但定义了允许你修改资源/与之交互的固定HTTP操作。可以在RESTful API中访问以下HTTP操作: • GET请求:检索资源 • POST请求:创建资源或将信息发送到API • PUT请求:创建或替换资源 • PATCH请求:更新现有资源 • DELETE请求:删除资源 在对RESTful API的特性有了更深入的了解后,是时候了解更多关于RESTful API的最佳实践了。 本文为你提供了13种最佳实践的可行清单。让我们来探索!</li> </ol><p>1.正确使用HTTP方法 我们已经讨论了可用于修改资源的HTTP方法:GET,POST,PUT,PATCH和DELETE。 尽管如此,许多开发人员还是倾向于滥用GET和POST或PUT和PATCH。通常,我们看到开发人员使用POST请求来检索数据。此外,我们看到开发人员使用PUT请求来替换资源,而他们只想更新该资源的单个字段。 确保使用正确的HTTP方法,因为这将为使用你的RESTful API的开发人员增加很多混乱。最好是坚持使用预定的准则。</p> <p>2.命名约定 了解RESTful API命名约定将对你有组织地设计API有很大帮助。根据你服务的资源设计一个RESTful API。 例如,你的API管理着作者和书籍(是的,一个经典的例子)。现在,我们要添加一个新作者或访问一个ID为 3 的作者。你可以设计下面的路由来达到这个目的: • api.com/addNewAuthor • api.com/getAuthorByID/3 想象一下,一个API承载了许多资源,每个资源都有许多属性。可能的端点列表将变得无穷无尽,而且对用户不是很友好。所以我们需要一种更有条理和标准化的方式来设计API端点。 RESTful API最佳实践描述了端点应以资源名称开头,而HTTP操作则描述操作。现在我们得到: • POST api.com/authors • GET api.com/authors/3 如果我们想访问ID为 3 的作者曾经写过的所有书籍怎么办?对于这种情况,RESTful API也有解决办法: • GET api.com/authors/3/books 最后,如果您要为ID为 3 的作者删除ID为 5 的书,该怎么办?同样,让我们遵循相同的结构化方法来形成以下端点: • DELETE api.com/authors/3/books/5 简而言之,利用HTTP操作和资源映射的结构化方式来形成易于理解的端点路径。这种方法的最大优点是,每个开发人员都了解RESTful API的设计方式,他们可以立即使用API,而不必阅读你的每个端点的文档。</p> <p>3.使用复数资源 资源应始终使用其复数形式。为什么?假设你要检索所有作者。因此,你将调用以下端点:GET api.com/authors。 当你读取请求时,你无法判断API响应是否只包含一个或所有作者。因此,API端点应该使用复数资源。</p> <p>4.正确使用状态码 状态码在这里不只是为了好玩,它们有一个明确的目的,状态码通知客户端请求的成功。 最常见的状态码类别包括: • 200(OK):请求已成功处理并完成。 • 201(Created):指示成功创建资源。 • 400(Bad Request):代表客户端错误。也就是说,请求的格式不正确或缺少请求参数。 • 401(Unauthorized):未授权,你尝试访问你没有权限的资源。 • 404(Not Found):请求的资源不存在。 • 500(Internal Server Error):内部服务器错误,服务器在执行请求期间引发异常。 状态码的完整列表可以在Mozilla Developers[1]找到。</p> <p>5.遵循相同约定 最常见的是,RESTful API提供JSON数据,因此,应遵循camelCase大小写惯例。但是,不同的编程语言使用不同的命名约定。</p> <p>6.如何处理搜索,分页,过滤和排序 搜索,分页,过滤和排序等操作并不代表单独的端点。这些操作可以通过使用随API请求提供的查询参数来完成。 例如,让我们检索按名称升序排列的所有作者。你的API请求应如下所示:api.com/authors?sort=name_asc。 此外,我想检索一个名称为“ Michiel”的作者。该请求看起来像这样 api.com/authors?search=Michiel。 幸运的是,许多API项目都带有内置的搜索、分页、过滤和排序功能。这将为你节省很多时间。</p> <p>7.API版本控制 我不常看到这一点,但这是对你的API进行版本调整的最佳实践。这是一种有效的方式来向你的用户传达重大的变化。 通常,API的版本号包含在API URL中,例如:api.com/v1/authors/3/books。</p> <p>8.通过HTTP标头发送元数据 HTTP标头允许客户端随其请求发送其他信息。例如,Authorization 标头通常用于发送身份验证数据以访问API。 你可以在此处[2]找到所有可能的HTTP标头的完整列表。</p> <p>9.限速 速率限制是控制每个客户端请求数量的一种有趣方法。这些是服务器可能返回的速率限制标头: • X-Rate-Limit-Limit:告诉客户端在指定时间间隔内可以发送的请求数。 • X-Rate-Limit-Remaining:告诉客户端在当前时间间隔内仍可以发送多少个请求。 • X-Rate-Limit-Reset:告诉客户端速率限制何时重置。 • 10.有意义的错误处理 如果出现问题,请务必向开发人员提供有意义的错误消息,这一点很重要。例如,Twilio API返回以下错误格式: <img alt="" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/up-49d2cf1dea4206534d2a31306bc4493bc59.png" data-original="https://oscimg.oschina.net/oscnet/up-49d2cf1dea4206534d2a31306bc4493bc59.png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>在此示例中,服务器返回状态代码和人类可读的消息。此外,还返回内部错误代码,供开发人员查找特定错误,这使开发人员可以快速查找有关该错误的更多信息。</p> <p>11.选择正确的API框架 存在许多用于不同编程语言的框架,选择一个支持RESTful API最佳做法的框架非常重要。 对于Node.js,后端开发人员喜欢使用Express.js和Koa,而对于Python,Falcon是一个不错的选择。</p> <p>12.文档化你的API 最后,写文档!我不是在开玩笑,这仍然是传递你新开发的API知识最简单的方法之一。 尽管你的API遵循RESTful API列出的所有最佳实践,但仍然值得你花时间记录各种元素,比如API处理的资源或应用于服务器的速率限制。 想想你的其他开发人员,文档大大减少了学习API所需的时间。</p> <p>13.把事情简单化! 不要让你的API过于复杂,保持资源简单。正确定义你的API处理的不同资源,将帮助你在未来避免资源相关的问题。定义你的资源,还要准确定义它的属性和资源之间的关系。这样一来,如何连接不同的资源就没有争议的空间了。</p> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4663426/blog/4835788</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/python" hreflang="zh-hans">python</a></div> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> <div class="field--item"><a href="/tag/github" hreflang="zh-hans">github</a></div> <div class="field--item"><a href="/tag/falcon" hreflang="zh-hans">falcon</a></div> <div class="field--item"><a href="/tag/koa" hreflang="zh-hans">koa</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> </div> </div> Wed, 23 Dec 2020 11:45:27 +0000 ﹥>﹥吖頭↗ 3975110 at https://www.e-learn.cn 再也不怕面试官问你express和koa的区别了 https://www.e-learn.cn/topic/3861992 <span>再也不怕面试官问你express和koa的区别了</span> <span><span lang="" about="/user/97" typeof="schema:Person" property="schema:name" datatype="">两盒软妹~`</span></span> <span>2020-10-16 07:47:44</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h2_1"></span> <h2>前言</h2> <p>用了那么多年的express.js,终于有时间来深入学习express,然后顺便再和koa2的实现方式对比一下。</p> <p>老实说,还没看express.js源码之前,一直觉得express.js还是很不错的,无论从api设计,还是使用上都是可以的。但是这次阅读完express代码之后,我可能改变想法了。</p> <p>虽然express.js有着精妙的中间件设计,但是以当前js标准来说,这种精妙的设计在现在可以说是太复杂。里面的层层回调和递归,不花一定的时间还真的很难读懂。而koa2的代码呢?简直可以用四个字评论:精简彪悍!仅仅几个文件,用上最新的js标准,就很好实现了中间件,代码读起来一目了然。</p> <p>老规矩,读懂这篇文章,我们依然有一个简单的demo来演示: <a href="https://link.zhihu.com/?target=https%3A//github.com/linxiaowu66/express-vs-koa" rel="nofollow">express-vs-koa</a></p> <span id="OSC_h2_2"></span> <h2>1、express用法和koa用法简单展示</h2> <p>如果你使用express.js启动一个简单的服务器,那么基本写法应该是这样:</p> <pre><code>const express = require('express') const app = express() const router = express.Router() app.use(async (req, res, next) =&gt; { console.log('I am the first middleware') next() console.log('first middleware end calling') }) app.use((req, res, next) =&gt; { console.log('I am the second middleware') next() console.log('second middleware end calling') }) router.get('/api/test1', async(req, res, next) =&gt; { console.log('I am the router middleware =&gt; /api/test1') res.status(200).send('hello') }) router.get('/api/testerror', (req, res, next) =&gt; { console.log('I am the router middleware =&gt; /api/testerror') throw new Error('I am error.') }) app.use('/', router) app.use(async(err, req, res, next) =&gt; { if (err) { console.log('last middleware catch error', err) res.status(500).send('server Error') return } console.log('I am the last middleware') next() console.log('last middleware end calling') }) app.listen(3000) console.log('server listening at port 3000') </code></pre> <p>换算成等价的koa2,那么用法是这样的:</p> <pre><code>const koa = require('koa') const Router = require('koa-router') const app = new koa() const router = Router() app.use(async(ctx, next) =&gt; { console.log('I am the first middleware') await next() console.log('first middleware end calling') }) app.use(async (ctx, next) =&gt; { console.log('I am the second middleware') await next() console.log('second middleware end calling') }) router.get('/api/test1', async(ctx, next) =&gt; { console.log('I am the router middleware =&gt; /api/test1') ctx.body = 'hello' }) router.get('/api/testerror', async(ctx, next) =&gt; { throw new Error('I am error.') }) app.use(router.routes()) app.listen(3000) console.log('server listening at port 3000') </code></pre> <p>如果你还感兴趣原生nodejs启动服务器是怎么使用的,可以参考demo中的这个文件:<a href="https://link.zhihu.com/?target=https%3A//github.com/linxiaowu66/express-vs-koa/blob/master/node.js" rel="nofollow">node.js</a></p> <p>于是二者的使用区别通过表格展示如下(知乎不支持markdown也是醉了~表格只能截图了~):</p> <p><img alt="" width="686" class="b-lazy" data-src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWMxLnpoaW1nLmNvbS84MC92Mi1lNGIzZDhkN2I0MDRmZWRjYWEyNTMwNWQ4ZjZlYTczYl8xNDQwdy5qcGc?x-oss-process=image/format,png" data-original="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWMxLnpoaW1nLmNvbS84MC92Mi1lNGIzZDhkN2I0MDRmZWRjYWEyNTMwNWQ4ZjZlYTczYl8xNDQwdy5qcGc?x-oss-process=image/format,png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>上表展示了二者的使用区别,从初始化就看出koa语法都是用的新标准。在挂载路由中间件上也有一定的差异性,这是因为二者内部实现机制的不同。其他都是大同小异的了。</p> <p>那么接下去,我们的重点便是放在二者的中间件的实现上。</p> <span id="OSC_h2_3"></span> <h2>2、express.js中间件实现原理</h2> <p>我们先来看一个demo,展示了express.js的中间件在处理某些问题上的弱势。demo代码如下:</p> <pre><code>const express = require('express') const app = express() const sleep = (mseconds) =&gt; new Promise((resolve) =&gt; setTimeout(() =&gt; { console.log('sleep timeout...') resolve() }, mseconds)) app.use(async (req, res, next) =&gt; { console.log('I am the first middleware') const startTime = Date.now() console.log(`================ start ${req.method} ${req.url}`, { query: req.query, body: req.body }); next() const cost = Date.now() - startTime console.log(`================ end ${req.method} ${req.url} ${res.statusCode} - ${cost} ms`) }) app.use((req, res, next) =&gt; { console.log('I am the second middleware') next() console.log('second middleware end calling') }) app.get('/api/test1', async(req, res, next) =&gt; { console.log('I am the router middleware =&gt; /api/test1') await sleep(2000) res.status(200).send('hello') }) app.use(async(err, req, res, next) =&gt; { if (err) { console.log('last middleware catch error', err) res.status(500).send('server Error') return } console.log('I am the last middleware') await sleep(2000) next() console.log('last middleware end calling') }) app.listen(3000) console.log('server listening at port 3000') </code></pre> <p>该demo中当请求<code>/api/test1</code>的时候打印结果是什么呢?</p> <pre><code>I am the first middleware ================ start GET /api/test1 I am the second middleware I am the router middleware =&gt; /api/test1 second middleware end calling ================ end GET /api/test1 200 - 3 ms sleep timeout...</code></pre> <p>如果你清楚这个打印结果的原因,想必对express.js的中间件实现有一定的了解。</p> <p>我们先看看第一节demo的打印结果是:</p> <pre><code>I am the first middleware I am the second middleware I am the router middleware =&gt; /api/test1 second middleware end calling first middleware end calling</code></pre> <p>这个打印符合大家的期望,但是为什么刚才的demo打印的结果就不符合期望了呢?二者唯一的区别就是第二个demo加了异步处理。有了异步处理,整个过程就乱掉了。因为我们期望的执行流程是这样的:</p> <pre><code>I am the first middleware ================ start GET /api/test1 I am the second middleware I am the router middleware =&gt; /api/test1 sleep timeout... second middleware end calling ================ end GET /api/test1 200 - 3 ms</code></pre> <p>那么是什么导致这样的结果呢?我们在接下去的分析中可以得到答案。</p> <span id="OSC_h3_4"></span> <h3>2.1、express挂载中间件的方式</h3> <p>要理解其实现,我们得先知道express.js到底有多少种方式可以挂载中间件进去?熟悉express.js的童鞋知道吗?知道的童鞋可以心里默默列举一下。</p> <p>目前可以挂载中间件进去的有:(HTTP Method指代那些http请求方法,诸如Get/Post/Put等等)</p> <ul><li>app.use</li> <li>app.[HTTP Method]</li> <li>app.all</li> <li>app.param</li> <li>router.all</li> <li>router.use</li> <li>router.param</li> <li>router.[HTTP Method]</li> </ul><span id="OSC_h3_5"></span> <h3>2.2、express中间件初始化</h3> <p>express代码中依赖于几个变量(实例):app、router、layer、route,这几个实例之间的关系决定了中间件初始化后形成一个数据模型,画了下面一张图片来展示:</p> <p> </p> <p><img alt="" width="1200" class="b-lazy" data-src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWM0LnpoaW1nLmNvbS84MC92Mi04YjA2ZDNhYjgxMmYyY2NhYjdjZWU2MWMxZmM4NjQ0MV8xNDQwdy5qcGc?x-oss-process=image/format,png" data-original="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWM0LnpoaW1nLmNvbS84MC92Mi04YjA2ZDNhYjgxMmYyY2NhYjdjZWU2MWMxZmM4NjQ0MV8xNDQwdy5qcGc?x-oss-process=image/format,png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p> </p> <p>图中存在两块Layer实例,挂载的地方也不一样,以<a href="https://link.zhihu.com/?target=https%3A//github.com/linxiaowu66/express-vs-koa/blob/master/express.js" rel="nofollow">express.js</a>为例子,我们通过调试找到更加形象的例子:</p> <p> </p> <p><img alt="" width="640" class="b-lazy" data-src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWMzLnpoaW1nLmNvbS84MC92Mi0yMmZiOTQ3YjM4YmVlMTdmOTJiYmE0OGY0NTY3MDMzOV8xNDQwdy5qcGc?x-oss-process=image/format,png" data-original="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWMzLnpoaW1nLmNvbS84MC92Mi0yMmZiOTQ3YjM4YmVlMTdmOTJiYmE0OGY0NTY3MDMzOV8xNDQwdy5qcGc?x-oss-process=image/format,png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p> </p> <p>结合二者,我们来聊聊express中间件初始化。<em>为了方便,我们把上图1叫做初始化模型图,上图2叫做初始化实例图</em></p> <p>看上面两张图,我们抛出下面几个问题,搞懂问题便是搞懂了初始化。</p> <ul><li>初始化模型图Layer实例为什么分两种?</li> <li>初始化模型图Layer实例中route字段什么时候会存在?</li> <li>初始化实例图中挂载的中间件为什么有7个?</li> <li>初始化实例图中圈2和圈3的route字段不一样,而且name也不一样,为什么?</li> <li>初始化实例图中的圈4里也有Layer实例,这个时候的Layer实例和上面的Layer实例不一样吗?</li> </ul><p>首先我们先输出这样的一个概念:Layer实例是path和handle互相映射的实体,每一个Layer便是一个中间件。</p> <p>这样的话,我们的中间件中就有可能嵌套中间件,那么对待这种情形,express就在Layer中做手脚。我们分两种情况挂载中间件:</p> <ul><li>使用<code>app.use</code>、<code>router.use</code>来挂载的 <ul><li><code>app.use</code>经过一系列处理之后最终也是调用<code>router.use</code>的</li> </ul></li> <li>使用<code>app.all</code>、<code>app.[Http Method]</code>、<code>app.route</code>、<code>router.all</code>、<code>router.[Http Method]</code>、<code>router.route</code>来挂载的 <ul><li><code>app.all</code>、<code>app.[Http Method]</code>、<code>app.route</code>、<code>router.all</code>、<code>router.[Http Method]</code>经过一系列处理之后最终也是调用<code>router.route</code>的</li> </ul></li> </ul><p>因此我们把焦点聚焦在<code>router.use</code>和<code>router.route</code>这两个方法。</p> <span id="OSC_h3_6"></span> <h3>2.2.1、router.use</h3> <p>该方法的最核心一段代码是:</p> <pre><code>for (var i = 0; i &lt; callbacks.length; i++) { var fn = callbacks[i]; if (typeof fn !== 'function') { throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn)) } // add the middleware debug('use %o %s', path, fn.name || '&lt;anonymous&gt;') var layer = new Layer(path, { sensitive: this.caseSensitive, strict: false, end: false }, fn); // 注意这个route字段设置为undefined layer.route = undefined; this.stack.push(layer); } </code></pre> <p>此时生成的Layer实例对应的便是<strong>初始化模型图1指示的多个Layer实例</strong>,此时以<a href="https://link.zhihu.com/?target=https%3A//github.com/linxiaowu66/express-vs-koa/blob/master/express.js" rel="nofollow">express.js</a>为例子,我们看<strong>初始化实例图圈1的所有Layer实例</strong>,会发现除了我们自定义的中间件(共5个),还有两个系统自带的,看初始化实例图的Layer的名字分别是:<code>query</code>和<code>expressInit</code>。二者的初始化是在[application.js]中的<code>lazyrouter</code>方法:</p> <pre><code>app.lazyrouter = function lazyrouter() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') }); this._router.use(query(this.get('query parser fn'))); // 最终调用的就是router.use方法 this._router.use(middleware.init(this)); // 最终调用的就是router.use方法 } }; </code></pre> <p><strong>于是回答了我们刚才的第三个问题。7个中间件,2个系统自带、3个APP级别的中间、2个路由级别的中间件</strong></p> <span id="OSC_h3_7"></span> <h3>2.2.2、router.route</h3> <p>我们说过<code>app.all</code>、<code>app.[Http Method]</code>、<code>app.route</code>、<code>router.all</code>、<code>router.[Http Method]</code>经过一系列处理之后最终也是调用<code>router.route</code>的,所以我们在demo中的<a href="https://link.zhihu.com/?target=https%3A//github.com/linxiaowu66/express-vs-koa/blob/master/express.js" rel="nofollow">express.js</a>,使用了两次<code>app.get</code>,其最后调用了<code>router.route</code>,我们看该方法核心实现:</p> <pre><code>proto.route = function route(path) { var route = new Route(path); var layer = new Layer(path, { sensitive: this.caseSensitive, strict: this.strict, end: true }, route.dispatch.bind(route)); layer.route = route; this.stack.push(layer); return route; }; </code></pre> <p>这么简单的实现,与上一个方法的实现唯一的区别就是多了<code>new Route</code>这个。通过二者对比,我们可以回答上面的好几个问题:</p> <ul><li>初始化模型图Layer实例为什么分两种? 因为调用方式的不同决定了Layer实例的不同,第二种Layer实例是挂载在route实例之下的。</li> <li>初始化模型图Layer实例中route字段什么时候会存在?使用<code>router.route</code>的时候就会存在</li> <li>初始化实例图中圈2和圈3的route字段不一样,而且name也不一样,为什么?圈2的Layer因为我们使用箭头函数,不存在函数名,所以name是<code>anonymous</code>,但是圈3因为使用的<code>router.route</code>,所以其统一的回调函数都是<code>route.dispath</code>,因此其函数名字都统一是<code>bound dispatch</code>,同时二者的route字段是否赋值也一目了然</li> </ul><p>最后一个问题,既然实例化route之后,route有了自己的Layer,那么它的初始化又是在哪里的?初始化核心代码:</p> <pre><code>// router/route.js/Route.prototype[method] for (var i = 0; i &lt; handles.length; i++) { var handle = handles[i]; if (typeof handle !== 'function') { var type = toString.call(handle); var msg = 'Route.' + method + '() requires a callback function but got a ' + type throw new Error(msg); } debug('%s %o', method, this.path) var layer = Layer('/', {}, handle); layer.method = method; this.methods[method] = true; this.stack.push(layer); } </code></pre> <p>可以看到新建的route实例,维护的是一个path,对应多个method的handle的映射。每一个method对应的handle都是一个layer,path统一为<code>/</code>。这样就轻松回答了最后一个问题了。</p> <p>至此,再回去看初始化模型图,相信大家可以有所明白了吧~</p> <span id="OSC_h3_8"></span> <h3>2.3、express中间件的执行逻辑</h3> <p>整个中间件的执行逻辑无论是外层Layer,还是route实例的Layer,都是采用递归调用形式,一个非常重要的函数<code>next()</code>实现了这一切,这里做了一张流程图,希望对你理解这个有点用处:</p> <p> </p> <p><img alt="" width="1200" class="b-lazy" data-src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWM0LnpoaW1nLmNvbS84MC92Mi02OTU4OGI3MGJjM2RiOTFjMGMyMjFjMWUyMjc3ODBiMl8xNDQwdy5qcGc?x-oss-process=image/format,png" data-original="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWM0LnpoaW1nLmNvbS84MC92Mi02OTU4OGI3MGJjM2RiOTFjMGMyMjFjMWUyMjc3ODBiMl8xNDQwdy5qcGc?x-oss-process=image/format,png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p> </p> <p>我们再把<a href="https://link.zhihu.com/?target=https%3A//github.com/linxiaowu66/express-vs-koa/blob/master/express.js" rel="nofollow">express.js</a>的代码使用另外一种形式实现,这样你就可以完全搞懂整个流程了。</p> <p>为了简化,我们把系统挂载的两个默认中间件去掉,把路由中间件去掉一个,最终的效果是:</p> <pre><code>((req, res) =&gt; { console.log('I am the first middleware'); ((req, res) =&gt; { console.log('I am the second middleware'); (async(req, res) =&gt; { console.log('I am the router middleware =&gt; /api/test1'); await sleep(2000) res.status(200).send('hello') })(req, res) console.log('second middleware end calling'); })(req, res) console.log('first middleware end calling') })(req, res) </code></pre> <p>因为没有对await或者promise的任何处理,所以当中间件存在异步函数的时候,因为整个next的设计原因,并不会等待这个异步函数resolve,于是我们就看到了<code>sleep</code>函数的打印被放在了最后面,并且第一个中间件想要记录的请求时间也变得不再准确了~</p> <p><strong>但是有一点需要申明的是虽然打印变得奇怪,但是绝对不会影响整个请求,因为response是在我们await之后,所以请求是否结束还是取决于我们是否调用了res.send这类函数</strong></p> <p>至此,希望整个express中间件的执行流程你可以熟悉一二,更多细节建议看看源码,这种精妙的设计确实不是这篇文章能够说清楚的。本文只是想你在面试的过程中可以做到有话要说~</p> <p>接下去,我们分析牛逼的Koa2,这个就不需要费那么大篇幅去讲,因为实在是太太容易理解了。</p> <span id="OSC_h2_9"></span> <h2>3、koa2中间件</h2> <p>koa2中间件的主处理逻辑放在了<a href="https://link.zhihu.com/?target=https%3A//github.com/koajs/compose" rel="nofollow">koa-compose</a>,也就是仅仅一个函数的事情:</p> <pre><code>function compose (middleware) { if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } /** * @param {Object} context * @return {Promise} * @api public */ return function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i &lt;= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } } </code></pre> <p>每个中间件调用的next()其实就是这个:</p> <pre><code>dispatch.bind(null, i + 1)</code></pre> <p>还是利用闭包和递归的性质,一个个执行,并且每次执行都是返回promise,所以最后得到的打印结果也是如我们所愿。那么路由的中间件是否调用就不是koa2管的,这个工作就交给了<code>koa-router</code>,这样koa2才可以保持精简彪悍的风格。</p> <p>再贴出koa中间件的执行流程吧:</p> <p><img alt="" class="b-lazy" data-src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWNiLnpoaW1nLmNvbS92Mi1iMzUzZTdlNDU4ZDc1NDJiMzY2YzExMzliMDFiMWEwN19iLndlYnA?x-oss-process=image/format,png" data-original="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWNiLnpoaW1nLmNvbS92Mi1iMzUzZTdlNDU4ZDc1NDJiMzY2YzExMzliMDFiMWEwN19iLndlYnA?x-oss-process=image/format,png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p> </p> <span id="OSC_h2_10"></span> <h2>最后</h2> <p>有了这篇文章,相信你再也不怕面试官问你express和koa的区别了~</p> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4351890/blog/4436987</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/layer" hreflang="zh-hans">layer</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> </div> </div> Thu, 15 Oct 2020 23:47:44 +0000 两盒软妹~` 3861992 at https://www.e-learn.cn npm WARN package.json:没有存储库字段 https://www.e-learn.cn/topic/3761743 <span>npm WARN package.json:没有存储库字段</span> <span><span lang="" about="/user/196" typeof="schema:Person" property="schema:name" datatype="">好久不见.</span></span> <span>2020-08-16 03:17:11</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h3_1"></span> <h3>问题:</h3> <p> <i>I installed Express.js with the following command:</i> <b>我使用以下命令安装了Express.js:</b> </p> <pre><code>sudo npm install -g express </code></pre> <p> <i>I get the following warnings:</i> <b>我收到以下警告:</b> </p> <pre><code>npm WARN package.json range-parser@0.0.4 No repository field. npm WARN package.json fresh@0.1.0 No repository field. npm WARN package.json methods@0.0.1 No repository field. npm WARN package.json methods@0.0.1 No readme data. npm WARN package.json cookie-signature@1.0.1 No repository field. npm WARN package.json send@0.1.0 No repository field. npm WARN package.json pause@0.0.1 No repository field. npm WARN package.json bytes@0.2.0 No repository field. npm WARN package.json github-url-from-git@1.1.1 No repository field. npm WARN package.json assert-plus@0.1.2 No repository field. npm WARN package.json ctype@0.5.2 No repository field. </code></pre> <p> <i>I'm new to Node.js and Express.js.</i> <b>我是Node.js和Express.js的新手。</b> <i>Why do I have the above warnings?</i> <b>为什么我有上述警告?</b> <i>Should I be worried?</i> <b>我应该担心吗?</b> </p> <hr /><span id="OSC_h3_2"></span> <h3>解决方案:</h3>参考一: <a href="https://stackoom.com/question/18bh4/npm-WARN-package-json-没有存储库字段" target="_blank" rel="nofollow">https://stackoom.com/question/18bh4/npm-WARN-package-json-没有存储库字段</a> <br />参考二: <a href="https://oldbug.net/q/18bh4/npm-WARN-package-json-No-repository-field" target="_blank" rel="nofollow">https://oldbug.net/q/18bh4/npm-WARN-package-json-No-repository-field</a> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4428122/blog/4330822</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/npm" hreflang="zh-hans">npm</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> </div> </div> Sat, 15 Aug 2020 19:17:11 +0000 好久不见. 3761743 at https://www.e-learn.cn Nodejs https://www.e-learn.cn/topic/3760964 <span>Nodejs</span> <span><span lang="" about="/user/230" typeof="schema:Person" property="schema:name" datatype="">不问归期</span></span> <span>2020-08-15 19:25:24</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h1_1"></span> <h1>新建一个 nodejs 项目.</h1> <span id="OSC_h2_2"></span> <h2>Pre install: </h2> <ul><li>Nodejs </li> <li>npm  - </li> </ul><span id="OSC_h2_3"></span> <h2>Steps: </h2> <ol><li>mkdir nodejs-hw-pure</li> <li>cd nodejs-hw-pure</li> <li>new file: <strong>hellonode.js</strong></li> </ol><pre> <code class="language-javascript">//引入required模块 var http = require("http"); //创建服务器 http.createServer(function(request,response){ //发送头部 response.writeHead(200,{'Content-Type':'text/plain'}); //发送响应数据 response.end('hello Nodejs...\n'); }).listen(8888); //终端打印以下信息 console.log('Server running at http://127.0.0.1:8888/');</code></pre> <p>4. Run:</p> <p>    $ node hellonode.js</p> <p>5. Verify on Browser (Chrome):  </p> <div> <div> <span style="color:#ce9178">        http://127.0.0.1:8888/</span> </div> </div> <p> </p> <span id="OSC_h2_4"></span> <h2>Use Express</h2> <pre> <code class="language-bash">$ npm install express --save $ npm install body-parser --save $ npm install cookie-parser --save $ npm install multer --save</code></pre> <p>new file:  <strong>helloworld-express.js</strong></p> <pre> <code class="language-javascript">var express = require("express"); var app = express(); app.get('/', function(req, res) { res.send('Hello Node by Express...'); }) var server = app.listen(8081, function() { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s;%s", host, port) })</code></pre> <p>Run:</p> <p>    $ node helloworld-express.js</p> <p>Verify on Browser (Chrome):  </p> <p>    <span style="color:#ce9178"> http://127.0.0.1:8081/</span></p> <p> </p> <p> </p> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/joryqiao/blog/4463642</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> <div class="field--item"><a href="/tag/chrome" hreflang="zh-hans">Chrome</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/javascript" hreflang="zh-hans">javascript</a></div> <div class="field--item"><a href="/tag/npm" hreflang="zh-hans">npm</a></div> </div> </div> Sat, 15 Aug 2020 11:25:24 +0000 不问归期 3760964 at https://www.e-learn.cn 为什么不将CORS标头添加到OPTIONS路由允许浏览器访问我的API? https://www.e-learn.cn/topic/3759193 <span>为什么不将CORS标头添加到OPTIONS路由允许浏览器访问我的API?</span> <span><span lang="" about="/user/136" typeof="schema:Person" property="schema:name" datatype="">扶醉桌前</span></span> <span>2020-08-15 07:53:39</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h3_1"></span> <h3>问题:</h3> <p> <i>I am trying to support CORS in my Node.js application that uses the Express.js web framework.</i> <b>我正在尝试在使用Express.js Web框架的Node.js应用程序中支持CORS。</b> <i>I have read <a href="http://groups.google.com/group/express-js/browse_thread/thread/71db58df2c74d06a" rel="nofollow">a Google group discussion</a> about how to handle this, and read a few articles about how CORS works.</i> <b>我已经阅读<a href="http://groups.google.com/group/express-js/browse_thread/thread/71db58df2c74d06a" rel="nofollow">了</a>有关如何处理此<a href="http://groups.google.com/group/express-js/browse_thread/thread/71db58df2c74d06a" rel="nofollow">问题的Google小组讨论</a> ,并阅读了一些有关CORS工作原理的文章。</b> <i>First, I did this (code is written in CoffeeScript syntax):</i> <b>首先,我做到了(代码是用CoffeeScript语法编写的):</b> </p> <pre><code>app.options "*", (req, res) -&gt; res.header 'Access-Control-Allow-Origin', '*' res.header 'Access-Control-Allow-Credentials', true # try: 'POST, GET, PUT, DELETE, OPTIONS' res.header 'Access-Control-Allow-Methods', 'GET, OPTIONS' # try: 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept' res.header 'Access-Control-Allow-Headers', 'Content-Type' # ... </code></pre> <p> <i>It doesn't seem to work.</i> <b>它似乎不起作用。</b> <i>It seems like my browser (Chrome) is not sending the initial OPTIONS request.</i> <b>看来我的浏览器(Chrome)没有发送初始的OPTIONS请求。</b> <i>When I just updated the block for the resource I need to submit a cross-origin GET request to:</i> <b>当我刚刚更新资源块时,我需要向以下站点提交跨域GET请求:</b> </p> <pre><code>app.get "/somethingelse", (req, res) -&gt; # ... res.header 'Access-Control-Allow-Origin', '*' res.header 'Access-Control-Allow-Credentials', true res.header 'Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS' res.header 'Access-Control-Allow-Headers', 'Content-Type' # ... </code></pre> <p> <i>It works (in Chrome).</i> <b>它可以工作(在Chrome中)。</b> <i>This also works in Safari.</i> <b>这也适用于Safari。</b> </p> <p> <i>I have read that...</i> <b>我读过...</b> </p> <blockquote> <p> <i>In a browser implementing CORS, each cross-origin GET or POST request is preceded by an OPTIONS request that checks whether the GET or POST is OK.</i> <b>在实现CORS的浏览器中,每个跨域的GET或POST请求之前都有一个OPTIONS请求,该请求检查GET或POST是否正常。</b> </p> </blockquote> <p> <i>So my main question is, how come this doesn't seem to happen in my case?</i> <b>所以我的主要问题是,在我看来,这种情况怎么似乎没有发生?</b> <i>Why isn't my app.options block called?</i> <b>为什么不调用我的app.options块?</b> <i>Why do I need to set the headers in my main app.get block?</i> <b>为什么需要在主app.get块中设置标题?</b> </p> <hr /><span id="OSC_h3_2"></span> <h3>解决方案:</h3>参考一: <a href="https://stackoom.com/question/TehS/为什么不将CORS标头添加到OPTIONS路由允许浏览器访问我的API" target="_blank" rel="nofollow">https://stackoom.com/question/TehS/为什么不将CORS标头添加到OPTIONS路由允许浏览器访问我的API</a> <br />参考二: <a href="https://oldbug.net/q/TehS/Why-doesn-t-adding-CORS-headers-to-an-OPTIONS-route-allow-browsers-to-access-my-API" target="_blank" rel="nofollow">https://oldbug.net/q/TehS/Why-doesn-t-adding-CORS-headers-to-an-OPTIONS-route-allow-browsers-to-access-my-API</a> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4428122/blog/4378223</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/chrome" hreflang="zh-hans">Chrome</a></div> <div class="field--item"><a href="/tag/coffeescript" hreflang="zh-hans">coffeescript</a></div> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> <div class="field--item"><a href="/tag/safari" hreflang="zh-hans">safari</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/framework" hreflang="zh-hans">Framework</a></div> </div> </div> Fri, 14 Aug 2020 23:53:39 +0000 扶醉桌前 3759193 at https://www.e-learn.cn Serverless Dashboard 设计解读与实战 https://www.e-learn.cn/topic/3699585 <span>Serverless Dashboard 设计解读与实战</span> <span><span lang="" about="/user/139" typeof="schema:Person" property="schema:name" datatype="">删除回忆录丶</span></span> <span>2020-07-27 09:09:18</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <p>作为腾讯云 Serverless 的产品经理,我经常会收集到小伙伴们在使用 Serverless Framework 的一些问题和吐槽,比如近期小伙伴们反馈:</p> <ol><li>依赖库安装和本地调试成功,但在云端部署为何失败?</li> <li>Serverless 应用内部的监控,无法直接查看,每次定位问题的流程好长啊!</li> <li>怎样组织 Serverless 应用?</li> <li>不同的函数之间的调用关系、环境划分、资源的管理及权限控制是怎样的呢?</li> </ol><p>近期 Serverless 团队发布了一款里程碑新特性产品,产品通过支持应用级别监控和 Dashboard 资源管理,有效解决小伙伴们的痛点问题,一起来看看吧!</p> <h2>Serverless Dashboard 新特性</h2> <h3>1. 应用管理</h3> <p>本次发布的应用管理页面则以 Component 为粒度,聚合了所有 Serverless Framework 部署的资源,并且展示了实例状态、访问链接以及上次的部署信息。此外,在管理详情中还支持删除 Serverless 应用、下载项目代码进行二次开发等操作,开发者可以更方便、集中的管理账号下的 Serverless 应用。如下图所示:</p> <p><img alt="" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/up-8f4b4fcaad78e47712b1cfa65b161abed8c.png" data-original="https://oscimg.oschina.net/oscnet/up-8f4b4fcaad78e47712b1cfa65b161abed8c.png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <h3>2. 部署详情及输出</h3> <p>Serverless Framework 的特性之一就是可以便捷的联动关联的云上资源,因此不同的 Serverless Component,可能会联动不同的云上资源,如网关、云函数、COS等。相信许多小伙伴在进行二次开发时,都想要了解每个 Component 具体创建了的资源信息。<br /> 在本次发布的部署详情页中,不仅可以查看到 Serverless 实例的基本信息,还可以在输出(output)页面中查看到 Serverless Component 对应的输入、输出信息。通过该页面,可以查看到对应的资源配置,如:地域信息、资源id、使用的语言环境、支持的协议信息等。有了这个页面,可以直观的看到对应的资源配置,再也不担心不同应用之间搞混配置啦。</p> <p><img alt="" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/up-05ad73d2d9b044c1a54ca16ab4cd46e8f44.JPEG" data-original="https://oscimg.oschina.net/oscnet/up-05ad73d2d9b044c1a54ca16ab4cd46e8f44.JPEG" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <h3>3. 应用级别监控</h3> <p>当前 Serverless Framework 已经支持了多种 Web 框架的一键部署。在部署完毕后,相信许多开发者会希望查看到基于应用级别的监控数据。而这往往在基础资源的监控中是难以体现出来的。</p> <p>那么本次发布最为亮眼的能力,即支持了应用级别的监控页面,实现了”0“配置的监控指标展示。当前已经支持 Express.js Component 的应用级别监控。无需去多个产品的控制台查看监控,无需自助上报数据,无需借助第三方 APM 插件,只需一次部署,立刻查看 Express 应用的监控信息!</p> <p><strong>当前的 Express.js 组件监控主要支持下列指标:</strong></p> <ul><li>函数触发次数/错误次数:function invocations &amp; errors</li> <li>函数延迟:function latency</li> <li>API 请求次数/错误次数:api requests &amp; errors</li> <li>API 请求延迟:api latency</li> <li>API 5xx 错误次数:api 5xx errors</li> <li>API 4xx 错误次数:api 4xx errors</li> <li>API 错误次数统计:api errors</li> <li>不同路径下 API 的请求方法、请求次数和平均延迟统计:api path requests</li> </ul><p><img alt="" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/up-fa7fb484bea9257adf807e65148352d832b.JPEG" data-original="https://oscimg.oschina.net/oscnet/up-fa7fb484bea9257adf807e65148352d832b.JPEG" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p><strong>由于 Serverless Dashboard 是基于新版的 Serverless Component 开发,因此同样支持新版 Serverless Component 的特性:</strong></p> <ol><li>【门槛低】交互式的一键部署指引:对于新用户而言,只需要在终端输入 serverless 命令,即可按照引导快速部署一个 Express 或 静态网站应用。</li> <li>【部署快】将一个 Express.js 应用部署到云端只需要 5-6s 的时间,使本地和云端代码可以顺畅、快速同步。</li> <li>【可复用】支持云端注册中心,每位开发者都可以贡献自己的组件到注册中心中,便于团队进行复用。</li> <li>【实时日志查看】支持部署阶段实时输出请求日志、错误等信息,此外支持检测本地代码变化并自动部署云端,方便的进行云端代码开发。</li> <li>【云端调试】针对 Node.js 应用,支持一键开启云端 debug 能力,对云端代码打断点调试,真正实现了在云端进行开发和调试的能力,无需考虑本地环境和远端环境的不一致问题。</li> <li>【状态共享】通过云端部署引擎存储应用部署状态,便于账号和团队之间共享资源,协作开发。</li> </ol><p>针对 Express.js 框架的应用级别监控主要基于腾讯云自定义监控能力实现。在部署过程中,框架中使用 Serverless SDK,收集应用级别的监控信息进行自定义上报和展示。因此用户可以做到 “0”配置 查看应用级别监控指标。真正实现快速部署一个开箱即用的 Serverless 应用框架。</p> <p>下面让我带大家一起实战体验一下我们的新产品吧!</p> <h2>玩转 Dashboard 使用实战</h2> <p>本次实战,我们将通过一个 Express.js 框架的部署,来体验 最新发布的 Dashboard 应用管理、监控视图等能力。</p> <p>首先,点击 <a href="https://serverless.cloud.tencent.com/deploy/express/" rel="nofollow">Express 链接</a>,扫码,登录腾讯云账号授权,一键部署你的 Express 应用。</p> <p>完成后,可以看到如下图所示:</p> <p><img alt="" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/up-f192cf43cddc36f7fb7260e937fd8828b6d.JPEG" data-original="https://oscimg.oschina.net/oscnet/up-f192cf43cddc36f7fb7260e937fd8828b6d.JPEG" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>你的 Express 应用已经部署好了!</p> <p>等待几分钟,就可以在 Dashboard 上看到对应的监控数据啦!</p> <p>如下图所示:</p> <p><img alt="" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/up-cacdeb4a7e9d608953e00ce31601554d42d.JPEG" data-original="https://oscimg.oschina.net/oscnet/up-cacdeb4a7e9d608953e00ce31601554d42d.JPEG" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>当前支持 15 分钟,60 分钟,24 小时和 7 天的监控数据。</p> <p><strong>如果您希望进行二次开发,则在本地安装 Serverless Framework,并点击右上角的<code>【下载项目代码】</code>,对代码进行修改和部署。</strong></p> <blockquote> <p>参考:<a href="https://cloud.tencent.com/product/sls" rel="nofollow">更多文档资料</a></p> </blockquote> <h2>Serverless Framework 30 天试用计划</h2> <p>我们诚邀您来体验最便捷的 Serverless 开发和部署方式。在试用期内,相关联的产品及服务均提供免费资源和专业的技术支持,帮助您的业务快速、便捷地实现 Serverless!</p> <blockquote> <p>详情可查阅:<a href="https://cloud.tencent.com/document/product/1154/38792" rel="nofollow">Serverless Framework 试用计划</a></p> </blockquote> <h2>One More Thing</h2> <p>3 秒你能做什么?喝一口水,看一封邮件,还是 —— 部署一个完整的 <a href="https://serverless.cloud.tencent.com/deploy/express" rel="nofollow">Serverless</a> 应用?</p> <blockquote> <p>复制链接至 PC 浏览器访问:<a href="https://serverless.cloud.tencent.com/deploy/express" rel="nofollow">https://serverless.cloud.tencent.com/deploy/express</a></p> </blockquote> <p>3 秒极速部署,立即体验史上最快的 <a href="https://serverless.cloud.tencent.com/deploy/express" rel="nofollow">Serverless HTTP</a> 实战开发!</p> <blockquote> <p><strong>传送门:</strong></p> <ul><li>GitHub: <a href="https://github.com/serverless/serverless/blob/master/README_CN.md" rel="nofollow">github.com/serverless</a></li> <li>官网:<a href="https://serverless.com/" rel="nofollow">serverless.com</a></li> </ul></blockquote> <p>欢迎访问:<a href="https://serverlesscloud.cn/" rel="nofollow">Serverless 中文网</a>,您可以在 <a href="https://serverlesscloud.cn/best-practice" rel="nofollow">最佳实践</a> 里体验更多关于 Serverless 应用的开发!</p> <hr /><blockquote> <p>推荐阅读:<a href="https://item.jd.com/12592747.html?cu=true&amp;utm_source=kong&amp;utm_medium=tuiguang&amp;utm_campaign=t_1001542270_1001895103_0_1912401775&amp;utm_term=536a21c6d22b4b48ba4c98e8045fd352" rel="nofollow">《Serverless 架构:从原理、设计到项目实战》</a></p> </blockquote> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/serverlesscloud/blog/4282607</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/framework" hreflang="zh-hans">Framework</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/utm" hreflang="zh-hans">utm</a></div> <div class="field--item"><a href="/tag/deploy" hreflang="zh-hans">Deploy</a></div> <div class="field--item"><a href="/tag/github" hreflang="zh-hans">github</a></div> <div class="field--item"><a href="/tag/serverless" hreflang="zh-hans">Serverless</a></div> </div> </div> Mon, 27 Jul 2020 01:09:18 +0000 删除回忆录丶 3699585 at https://www.e-learn.cn [转]Node.js框架对比:Express/Koa/Hapi https://www.e-learn.cn/topic/3597413 <span>[转]Node.js框架对比:Express/Koa/Hapi</span> <span><span lang="" about="/user/183" typeof="schema:Person" property="schema:name" datatype="">北城余情</span></span> <span>2020-04-28 03:20:45</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <p>本文转自:<a href="https://www.cnblogs.com/souvenir/p/6039990.html" target="_blank" rel="nofollow">https://www.cnblogs.com/souvenir/p/6039990.html</a></p> <p> </p> <blockquote> <p>本文翻译自: </p> <p><a href="https://www.airpair.com/node.js/posts/nodejs-framework-comparison-express-koa-hapi" target="_blank" rel="nofollow">https://www.airpair.com/node.js/posts/nodejs-framework-comparison-express-koa-hapi</a></p> </blockquote> <p>  </p> <span id="OSC_h2_1"></span> <h2>1、介绍</h2> <p>  直至今日,Express.js仍然是最为流行的Node.js Web应用程序框架。它似乎已经逐渐成为大多数Node.js Web应用程序的基础依赖框架,包括很多流行的框架,比如Sail.js就是以Express.js为基础搭建的。然而现在我们有了更多“类sinatra”<em>(注:sinatra是一款Ruby框架,代码非常简洁,号称开发一个博客项目只需要100行代码)</em>似的框架可以选择。也就是接下来我们将分别介绍的Koa和Hapi两个框架。</p> <p> </p> <p>  本文的目的并不是打算去说服大家去使用其中的任何一款框架,而是希望能够帮助大家去对比分析这三个框架的优劣势。</p> <p>  </p> <span id="OSC_h2_2"></span> <h2>2、框架背景</h2> <p> </p> <p>  今天我们对比的这三款框架其实都有很多的共通点。比如他们都可以几行代码就能创建一个服务,而且进行REST API的开发也是小菜一碟。下面我们就分别来看这三款框架吧。</p> <p> </p> <span id="OSC_h3_3"></span> <h3>2.1、Express</h3> <p> </p> <p>  2009年6月26日,TJ Holowaychuk 第一次提交了Express的代码。在2010年1月2日,Express正式发布了0.0.1版本,截止当时,作者已经提交了超过660次代码。当时Express的两位主要开发维护者分别是TJ 以及 Ciaron Jessup。第一版发布的时候,Express在Github的readme.md介绍文件中式这么描述这块框架的:</p> <blockquote> <p><em>一款基于node.js以及Chrome V8引擎,快速、极简的JS服务端开发框架。</em> </p> </blockquote> <p>  5年多后今天,Express目前已经发布到4.10.1版本,提交超过4925次代码,目前主要是采用StrongLoop进行开发维护,因为TJ同学已经转入GO语言开发社区了。</p> <p> </p> <span id="OSC_h3_4"></span> <h3>2.2、Koa</h3> <p>  Koa是在一年以前也就是在2013年8月17日由TJ同学(是的,还是他...)首次提交的代码。他当时是这么描述Koa的:“更具有表现力,更健壮的Node.js中间件。基于co组件的generators处理异步回调,无论是Web应用还是REST API开发,你的代码都将变得更加优雅”。<em>(注:Koa2发布后,已经放弃了引入co组件,而是开始采用ES7的async/await语法处理异步回调)。</em>轻量化的Koa号称不超过400行代码。<em>(注:SLOC是源代码行数,又分为物理代码行数LOC,以及逻辑代码行数LLOC)。</em>截止目前,Koa已经发布了0.13.0版本,超过585次的代码提交。</p> <p> </p> <span id="OSC_h3_5"></span> <h3>2.3、Hapi</h3> <p>  Hapi是由来自于沃尔玛实验室的Eran Hammer同学在2011年8月5日首次提交的。原本他只是<a href="https://github.com/hueniverse/postmile" rel="nofollow"><em>Postmile</em></a><em>(这是一款在node.js上开发的协作列表工具,</em>服务端由Hapi完成<em>)</em>的一个核心部件,同样也是基于Express开发。后来Hapi才被独立出来作为一款框架进行开发维护,Eran同学在他的博客里这样说道:</p> <blockquote> <p>  “Hapi的核心思想是配置优于代码,所以业务代码必须从传输层中剥离出来”</p> </blockquote> <p>  至今为止,Hapi已经提交超过3816次代码,版本是7.2.0,当前仍然是由Eran Hammer进行主要开发维护。</p> <p> </p> <p>  OK,最后让我们来通过社区的统计数据来看看这三个框架的活跃程度:</p> <p>  </p> <table cellspacing="0" cellpadding="0"><tbody><tr><td> <p><strong>参考项</strong></p> </td> <td> <p><strong>Express.js</strong></p> </td> <td> <p><strong>Koa.js</strong></p> </td> <td> <p><strong>Hapi.js</strong></p> </td> </tr><tr><td> <p><strong>Github点赞数</strong></p> </td> <td> <p>16158</p> </td> <td> <p>5846</p> </td> <td> <p>3283</p> </td> </tr><tr><td> <p><strong>代码贡献者</strong></p> </td> <td> <p>163</p> </td> <td> <p>49</p> </td> <td> <p>95</p> </td> </tr><tr><td> <p><strong>依赖包数量</strong></p> </td> <td> <p>3828</p> </td> <td> <p>99</p> </td> <td> <p>102</p> </td> </tr><tr><td> <p><strong>StackOverFlow提问数</strong></p> </td> <td> <p>11419</p> </td> <td> <p>72</p> </td> <td> <p>82</p> </td> </tr></tbody></table><p> </p> <span id="OSC_h2_6"></span> <h2>3、创建服务 </h2> <p>  基本上每个刚开始接触Node.js的开发者第一步操作就是创建一个服务。因为下面我们将依次使用每个框架来分别创建一个服务,来看看他们之间的相似处与不同的地方。</p> <p> </p> <span id="OSC_h3_7"></span> <h3>3.1、Express </h3> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/c26dcf3bebcc7d2e45b602d2c98cebf7388.gif" data-original="https://oscimg.oschina.net/oscnet/c26dcf3bebcc7d2e45b602d2c98cebf7388.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var express = require('express'); var app = express(); var server = app.listen(3000, function() { console.log('Express is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/edebcdb3baf55b8dbce1dbccfd067576d9b.gif" data-original="https://oscimg.oschina.net/oscnet/edebcdb3baf55b8dbce1dbccfd067576d9b.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p> </p> <p> </p> <p>  上面的操作对于大多数Node开发者来说应该都是很熟练了。我们先引入express,然后创建一个实例对象并将其赋值给变量app。接下来是实例化一个服务,并且开始监听3000端口。app.listen() 其实就是对nodejs原生的http.createServer()进行了一层封装。</p> <p> </p> <span id="OSC_h3_8"></span> <h3>3.2、Koa </h3> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/74fd8503025204d114e5e8d8edf5bfd3148.gif" data-original="https://oscimg.oschina.net/oscnet/74fd8503025204d114e5e8d8edf5bfd3148.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var koa = require('koa'); var app = koa(); var server = app.listen(3000, function() { console.log('Koa is listening to http://localhost:3000'); }); </code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/464fd021f2391064e7671cfa02820f97f19.gif" data-original="https://oscimg.oschina.net/oscnet/464fd021f2391064e7671cfa02820f97f19.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p>  显而易见,Koa的语法和Express非常相似。其实来说你只需要将引入express修改为引入koa即可。同样的,app.listen() 也是对http.createServer()进行了一层封装。</p> <p> </p> <span id="OSC_h3_9"></span> <h3>3.3、Hapi </h3> <p> </p> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/56b2abe103c51f3d532ebfc8fd3a72317b6.gif" data-original="https://oscimg.oschina.net/oscnet/56b2abe103c51f3d532ebfc8fd3a72317b6.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var Hapi = require('hapi'); var server = new Hapi.Server(3000); server.start(function() { console.log('Hapi is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/0820634ad903f1e580273811466b0f7ca7b.gif" data-original="https://oscimg.oschina.net/oscnet/0820634ad903f1e580273811466b0f7ca7b.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p> </p> <p>  Hapi的语法比较特别一些。不过,第一步还是引入hapi,但是这里是实例化存入一个hapi app变量中,然后就可以创建一个指定端口的服务了。在Express和Koa中这一步我们得到的是一个回调函数,但是Hapi返回的是一个server对象。一旦我们通过server.start()来调用这个在3000端口的服务以后,他将会返回一个回调函数。然后跟Koa和Express不一样的地方在于,这个回调并不是对http.CreateServer()进行的一层封装,而是Hapi自己实现的逻辑。</p> <p> </p> <span id="OSC_h2_10"></span> <h2>4、路由</h2> <p>  接下来我们继续深入了解作为一个服务的一个重要功能,那就是路由。第一步我们将使用每个框架来分别创建一个“Hello World”应用,然后再继续关注一些更实用的功能,REST API。</p> <p> </p> <span id="OSC_h3_11"></span> <h3>4.1 Hello World</h3> <p> </p> <span id="OSC_h4_12"></span> <h4>4.1.1  Express</h4> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/ccc407b4f5da83aa1347e10c9a2a6345d58.gif" data-original="https://oscimg.oschina.net/oscnet/ccc407b4f5da83aa1347e10c9a2a6345d58.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var express = require('express'); var app = express(); app.get('/', function(req, res) { res.send('Hello world'); }); var server = app.listen(3000, function() { console.log('Express is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/00a02c75b734ebf503362a5dfc5dbb956cb.gif" data-original="https://oscimg.oschina.net/oscnet/00a02c75b734ebf503362a5dfc5dbb956cb.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p> </p> <p>  我们使用get()方法来捕获“GET /”请求,然后调用一个回调函数来处理请求,该回调函数拥有两个参数:req与res。在这个例子中我们仅仅使用了res的res.send()方法来向页面返回一个字符串。Express包含了很多内置的方法来处理路由功能。下面是几个Express中常用的方法(只是部分,并不是全部方法):get, post, put, head, delete…</p> <p> </p> <span id="OSC_h4_13"></span> <h4>4.1.2 Koa </h4> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/c64511cd4ec0219a13dc1be414ff818dc5c.gif" data-original="https://oscimg.oschina.net/oscnet/c64511cd4ec0219a13dc1be414ff818dc5c.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var koa = require('koa'); var app = koa(); app.use(function *() { this.body = 'Hello world'; }); var server = app.listen(3000, function() { console.log('Koa is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/b0b5cb37fecc6e400c7fa007457f67f6e10.gif" data-original="https://oscimg.oschina.net/oscnet/b0b5cb37fecc6e400c7fa007457f67f6e10.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p>  Koa和Express有些许的不同之处,因为他使用了ES6 的generators语法。<em>(注:generators是ES6提出的一种异步回调的解决方法,在ES7中将直接升级为async/await)</em>在方法前面加上一个 * 表示该方法返回一个generator对象。generators函数的作用就是使得异步函数产生一些同步的值,但是这些值仍然是在当前的请求范围之类。<em>(注:generator对通过yield 定义不同的状态值,return也算是一个状态值。详情了解:</em><a href="http://es6.ruanyifeng.com/#docs/generator" rel="nofollow"><em>http://es6.ruanyifeng.com/#docs/generator</em></a><em> )</em>在app.use()中,generator函数对响应体进行赋值。在Koa中this对象,其实就是对node的request与response对象进行的封装。this.body在Koa中是一个响应体对象的方法。它基本上能被赋值为任何值,字符串、buffer、数据流、对象或者是null。Koa核心库提供了很多中间件,这里我们只是使用了其中的一个,这个中间件可以捕获所有的路由,然后响应一个字符串。</p> <p> </p> <span id="OSC_h4_14"></span> <h4>4.1.3 Hapi</h4> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/aa365c362d3950f1c51119b1e82565d1372.gif" data-original="https://oscimg.oschina.net/oscnet/aa365c362d3950f1c51119b1e82565d1372.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var Hapi = require('hapi'); var server = new Hapi.Server(3000); server.route({ method: 'GET', path: '/', handler: function(request, reply) { reply('Hello world'); } }); server.start(function() { console.log('Hapi is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/d9772543032f5ebd2b2aaf265305844775e.gif" data-original="https://oscimg.oschina.net/oscnet/d9772543032f5ebd2b2aaf265305844775e.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p>  这里我们使用了由server对象提供的一个内置方法:server.route(),这个方法需要这些参数:path(必填)、method(必填)、vhost以及handler(必填)。这个HTTP方法可以处理我们常见的GET/PUT/POST/DELETE请求,也可以使用*来处理所有路由请求。回调函数会被Hapi默认传入request对象以及reply方法,reply是必须被执行的方法,而且需要传入一项数据,这个数据可以是字符串、序列化的对象或者流。</p> <p>  </p> <span id="OSC_h3_15"></span> <h3>4.2 REST API</h3> <p>  Hello World程序从来都没有太多的期望,因为它只能展示创建及运行一个应用最基本最简单的操作。REST API几乎是所有大型应用程序所必须的一个功能,同时对于我们更好的理解这些框架有很大的帮助。因此接下来我们将看看这几个框架是如何来处理REST API。</p> <p> </p> <span id="OSC_h4_16"></span> <h4>4.2.1 Express  </h4> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/1d49603d53cd1ffd67e9c777436a0d7b3f1.gif" data-original="https://oscimg.oschina.net/oscnet/1d49603d53cd1ffd67e9c777436a0d7b3f1.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var express = require('express'); var app = express(); var router = express.Router(); // REST API router.route('/items') .get(function(req, res, next) { res.send('Get'); }) .post(function(req, res, next) { res.send('Post'); }); router.route('/items/:id') .get(function(req, res, next) { res.send('Get id: ' + req.params.id); }) .put(function(req, res, next) { res.send('Put id: ' + req.params.id); }) .delete(function(req, res, next) { res.send('Delete id: ' + req.params.id); }); app.use('/api', router); // index app.get('/', function(req, res) { res.send('Hello world'); }); var server = app.listen(3000, function() { console.log('Express is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/7fe3f3e6b0e95037204236c5040259d9d77.gif" data-original="https://oscimg.oschina.net/oscnet/7fe3f3e6b0e95037204236c5040259d9d77.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p>  我们在现有的Hello World程序上增加了REST API。Express提供了一些缩写的方法来处理路由。这是Express 4.x 版本的语法,其实跟Express 3.x 版本差不多,同样希望你不再使用express.Router()方法,而是换成新的API:app.use('/api', router)。新的API可以让我们使用app.route()来替换之前的router.route(),当然了需要添加一个描述性的动词/api.这是一个不错的修改,因为降低开发者出现错误的机会,同时对原有的HTTP方法进行了最小的一个修改。</p> <p> </p> <span id="OSC_h4_17"></span> <h4>4.2.2 Koa    </h4> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/065e7795a78309f1e21ca9402c52f1d8866.gif" data-original="https://oscimg.oschina.net/oscnet/065e7795a78309f1e21ca9402c52f1d8866.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var koa = require('koa'); var route = require('koa-route'); var app = koa(); // REST API app.use(route.get('/api/items', function*() { this.body = 'Get'; })); app.use(route.get('/api/items/:id', function*(id) { this.body = 'Get id: ' + id; })); app.use(route.post('/api/items', function*() { this.body = 'Post'; })); app.use(route.put('/api/items/:id', function*(id) { this.body = 'Put id: ' + id; })); app.use(route.delete('/api/items/:id', function*(id) { this.body = 'Delete id: ' + id; })); // all other routes app.use(function *() { this.body = 'Hello world'; }); var server = app.listen(3000, function() { console.log('Koa is listening to http://localhost:3000'); }); </code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/4495c2e96b82d54acf41d2f4405f8fc998d.gif" data-original="https://oscimg.oschina.net/oscnet/4495c2e96b82d54acf41d2f4405f8fc998d.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p>  很明显,Koa并不能像Express那样去降低route动词的重复性。它同时还需要引入一个独立的中间件来处理路由。我选择使用koa-route,是因为他主要是由Koa小组来开发维护,当然也还有很多其他开发者贡献的路由中间件可以选择。从方法名的关键字上来看,koa的路由和express也是非常相似的,例如.get(), .put(), .post(), 以及 .delete()。</p> <p>  Koa在处理路由有一个优势,它使用了ES6 的generator函数,从而降低了回调函数的复杂度。</p> <p> </p> <span id="OSC_h4_18"></span> <h4>4.2.3  Hapi   </h4> <div class="cnblogs_code"> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/4f66570dcf488ded6371f74788f72cbc1f2.gif" data-original="https://oscimg.oschina.net/oscnet/4f66570dcf488ded6371f74788f72cbc1f2.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> <pre><code>var Hapi = require('hapi'); var server = new Hapi.Server(3000); server.route([ { method: 'GET', path: '/api/items', handler: function(request, reply) { reply('Get item id'); } }, { method: 'GET', path: '/api/items/{id}', handler: function(request, reply) { reply('Get item id: ' + request.params.id); } }, { method: 'POST', path: '/api/items', handler: function(request, reply) { reply('Post item'); } }, { method: 'PUT', path: '/api/items/{id}', handler: function(request, reply) { reply('Put item id: ' + request.params.id); } }, { method: 'DELETE', path: '/api/items/{id}', handler: function(request, reply) { reply('Delete item id: ' + request.params.id); } }, { method: 'GET', path: '/', handler: function(request, reply) { reply('Hello world'); } } ]); server.start(function() { console.log('Hapi is listening to http://localhost:3000'); });</code></pre> <div class="cnblogs_code_toolbar"> <span><a rel="nofollow"><img alt="复制代码" class="b-lazy" data-src="https://oscimg.oschina.net/oscnet/10d0401741474a7fd88cafb66d4b2c698a1.gif" data-original="https://oscimg.oschina.net/oscnet/10d0401741474a7fd88cafb66d4b2c698a1.gif" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></a></span> </div> </div> <p>   跟其他框架相比,Hapi的路由配置给人的第一印象就是代码清爽,可读性高。甚至连必填的配置参数method,path,hanlder以及reply都非常容易辨别。跟Koa一样,Hapi路由的代码重复性也比较高,所以出错的几率也比较大。之所有这么做,是因为Hapi更希望使用配置来完成路由,这样我们的代码会更清爽,在小组内也会更容易的维护。Hapi同样试图去提高代码错误处理能力,因为有的时候他甚至不需要开发者编写任何代码<em>(注:意思是完全都过配置实现,回调函数也是用默认的。这样出错的 概率就</em>小<em>了很多,也更容易上手)</em>。如果你试图去访问一个没有在REST API中定义的路由,那么Hapi将会返回一个包含状态值与错误信息的JSON对象。</p> <p>  </p> <span id="OSC_h3_19"></span> <h3>5、优劣势 </h3> <p> </p> <span id="OSC_h3_20"></span> <h3>5.1 Express</h3> <p> </p> <span id="OSC_h4_21"></span> <h4>5.1.1 优势</h4> <p>  Express拥有最大社区,比仅仅是跟这三个框架相比,而是对于所有的Nodejs框架来说也是最大的。目前来说,他是最为三者中最为成熟的框架,接近5年的开发投入,同时还采用了StrongLoop<em>(注:StrongLoop是一个进程管理工具,提供CLI与UI界面。)</em>对线上仓库的代码进行管理。他提供了一种简单的方式来创建和运行一个服务,同时路由的内置也使得代码得到了重复使用。</p> <p> </p> <span id="OSC_h3_22"></span> <h3>5.1.2 劣势</h3> <p>  在使用Express过程中,我们往往要处理很多单调乏味的任务。比如他没有内置的错误处理机制,另外对于同样一个问题可以有很多中间件来供选择,这也使得开发者容易迷失在中间件的选择中,总而言之就是,一个问题你会有N多解决方案。Express声称自己是可配置选择的,这其实不没有好或不好,但是对于一个刚刚接触Express的开发者来说,这就是他的劣势了。另外,Express跟其他的框架相比也还有很大的差距。</p> <p>  </p> <span id="OSC_h3_23"></span> <h3>5.2 Koa</h3> <p> </p> <span id="OSC_h4_24"></span> <h4>5.2.1 优势</h4> <p>  Koa的一个小进步就是他的代码比较富有表现力,开发中间件也比其他框架更容易得多。Koa是一个很基础的准系统框架,开发者可以选择(或开发)他们所需要的中间件,而不是去选择Express或Hapi的中间件。他同时也是三者中唯一一个积极拥抱ES6的框架,比如采用了ES6 generators函数。</p> <p> </p> <span id="OSC_h4_25"></span> <h4>5.2.2 劣势 </h4> <p>  目前Koa还处于不稳定版本,还处在开发阶段。使用ES6进行开发的确是处于领先水平,比如Koa需要基于Nodejs 0.11.9以上的版本运行,而目前nodejs的文本版本是0.10.33。这是一件可以算作好也可以算作不好的事情,就像Express开发者有很多中间件要选择甚至自己开发中间件一样。比如我们在上面看到的一样,对于路由来说就有很多中间件供我们选择。</p> <p>  </p> <span id="OSC_h3_26"></span> <h3>5.3 Hapi</h3> <p> </p> <span id="OSC_h4_27"></span> <h4>5.3.1 优势</h4> <p>   Hapi一直很自豪的说他们的框架是配置优于代码,当然也有很多开发者可能会质疑把这一点算作是优势。但这一点对于大型项目组来说,的确是可以保持代码的统一性以及代码复用性。另外这款框架是由沃尔玛实验室支持的,也有很多大公司在线上环境使用Hapi,表明他已经通过了严峻的测试,因为这些公司会考虑得更多才会使用Hapi来运行他们的项目。因此所有的这些迹象都表明Hapi正在朝一个伟大的框架发展。</p> <p>  </p> <span id="OSC_h4_28"></span> <h4>5.3.2 劣势</h4> <p>   Hapi的定位更倾向于大型或复杂的应用程序。对于一个简单的应用来说,Hapi在代码上反而有些显得冗余了,另外目前Hapi所提供的样例程序也比较少,使用Hapi进行开发的开源应用同样很少。因此,如果选择Hapi的话,你可能要投入更多精力进行开发,而不是简单的调用一个第三方中间件。</p> <p> </p> <span id="OSC_h3_29"></span> <h3>6、总结</h3> <p>   我们已经看了三个框架还算不错具有代表性的一些样例代码。Express仍然是当下最为流行,以及最被人所知晓的框架。当开始一个新的开发项目时,可能大家的第一反应就是用Express来创建一个服务。但是现在更希望大家多考虑考虑使用Koa或者Hapi。Koa积极拥抱ES6的语法,展示了promise的真正魅力。目前整个web开发社区也都意识到ES6的优势,正在逐步往上面迁移。Hapi应该是大型项目组或者大型项目的第一选择。他所倡导的配置优于代码会使得项目组 在代码的重复性上受益不浅,这也正是大多数项目组所追求的目标。现在行动起来,尝试一款新的框架吧,可能你会喜欢他也可能会讨厌他,但如果不去尝试你永远也不会知道结果是什么,最终所有的这些经历都会让你成长为一个更加优秀的开发者。</p> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4324321/blog/3620975</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/next" hreflang="zh-hans">next</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/javascript" hreflang="zh-hans">javascript</a></div> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> <div class="field--item"><a href="/tag/github" hreflang="zh-hans">github</a></div> </div> </div> Mon, 27 Apr 2020 19:20:45 +0000 北城余情 3597413 at https://www.e-learn.cn 如何检索POST查询参数? https://www.e-learn.cn/topic/3596829 <span>如何检索POST查询参数?</span> <span><span lang="" about="/user/204" typeof="schema:Person" property="schema:name" datatype="">徘徊边缘</span></span> <span>2020-04-27 20:55:25</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h3_1"></span> <h3>问题:</h3> <p> <i>Here is my simple form:</i> <b>这是我的简单形式:</b> </p> <pre class="lang-html prettyprint-override"><code>&lt;form id="loginformA" action="userlogin" method="post"&gt; &lt;div&gt; &lt;label for="email"&gt;Email: &lt;/label&gt; &lt;input type="text" id="email" name="email"&gt;&lt;/input&gt; &lt;/div&gt; &lt;input type="submit" value="Submit"&gt;&lt;/input&gt; &lt;/form&gt; </code></pre> <p> <i>Here is my <a href="https://en.wikipedia.org/wiki/Express.js" rel="nofollow">Express.js</a> /Node.js code:</i> <b>这是我的<a href="https://en.wikipedia.org/wiki/Express.js" rel="nofollow">Express.js</a> /Node.js代码:</b> </p> <pre><code>app.post('/userlogin', function(sReq, sRes){ var email = sReq.query.email.; } </code></pre> <p> <i>I tried <code>sReq.query.email</code> or <code>sReq.query['email']</code> or <code>sReq.params['email']</code> , etc. None of them work.</i> <b>我尝试了<code>sReq.query.email</code>或<code>sReq.query['email']</code>或<code>sReq.params['email']</code>等。它们都不起作用。</b> <i>They all return <code>undefined</code> .</i> <b>他们都返回<code>undefined</code> 。</b> </p> <p> <i>When I change to a Get call, it works, so .. any idea?</i> <b>当我改为Get电话时,它有效,所以..任何想法?</b> </p> <hr /><span id="OSC_h3_2"></span> <h3>解决方案:</h3>参考一: <a href="https://stackoom.com/question/NxWY/如何检索POST查询参数" target="_blank" rel="nofollow">https://stackoom.com/question/NxWY/如何检索POST查询参数</a> <br />参考二: <a href="https://oldbug.net/q/NxWY/How-to-retrieve-POST-query-parameters" target="_blank" rel="nofollow">https://oldbug.net/q/NxWY/How-to-retrieve-POST-query-parameters</a> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4438370/blog/4255600</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/here" hreflang="zh-hans">Here</a></div> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> </div> </div> Mon, 27 Apr 2020 12:55:25 +0000 徘徊边缘 3596829 at https://www.e-learn.cn npm WARN package.json:没有存储库字段 https://www.e-learn.cn/topic/3593674 <span>npm WARN package.json:没有存储库字段</span> <span><span lang="" about="/user/97" typeof="schema:Person" property="schema:name" datatype="">两盒软妹~`</span></span> <span>2020-04-26 09:30:35</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <span id="OSC_h3_1"></span> <h3>问题:</h3> <p> <i>I installed Express.js with the following command:</i> <b>我使用以下命令安装了Express.js:</b> </p> <pre><code>sudo npm install -g express </code></pre> <p> <i>I get the following warnings:</i> <b>我收到以下警告:</b> </p> <pre><code>npm WARN package.json range-parser@0.0.4 No repository field. npm WARN package.json fresh@0.1.0 No repository field. npm WARN package.json methods@0.0.1 No repository field. npm WARN package.json methods@0.0.1 No readme data. npm WARN package.json cookie-signature@1.0.1 No repository field. npm WARN package.json send@0.1.0 No repository field. npm WARN package.json pause@0.0.1 No repository field. npm WARN package.json bytes@0.2.0 No repository field. npm WARN package.json github-url-from-git@1.1.1 No repository field. npm WARN package.json assert-plus@0.1.2 No repository field. npm WARN package.json ctype@0.5.2 No repository field. </code></pre> <p> <i>I'm new to Node.js and Express.js.</i> <b>我是Node.js和Express.js的新手。</b> <i>Why do I have the above warnings?</i> <b>为什么我有上述警告?</b> <i>Should I be worried?</i> <b>我应该担心吗?</b> </p> <hr /><span id="OSC_h3_2"></span> <h3>解决方案:</h3>参考一: <a href="https://stackoom.com/question/18bh4/npm-WARN-package-json-没有存储库字段" target="_blank" rel="nofollow">https://stackoom.com/question/18bh4/npm-WARN-package-json-没有存储库字段</a> <br />参考二: <a href="https://oldbug.net/q/18bh4/npm-WARN-package-json-No-repository-field" target="_blank" rel="nofollow">https://oldbug.net/q/18bh4/npm-WARN-package-json-No-repository-field</a> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/3797416/blog/4253696</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/npm" hreflang="zh-hans">npm</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/nodejs" hreflang="zh-hans">node.js</a></div> </div> </div> Sun, 26 Apr 2020 01:30:35 +0000 两盒软妹~` 3593674 at https://www.e-learn.cn 腾讯云正式发布 Serverless Framework https://www.e-learn.cn/topic/3588827 <span>腾讯云正式发布 Serverless Framework</span> <span><span lang="" about="/user/169" typeof="schema:Person" property="schema:name" datatype="">亡梦爱人</span></span> <span>2020-04-23 10:27:57</span> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"> <p>4 月 21 日 14:00,腾讯云召开了 Serverless Framework 线上发布会,会议邀请到了 serverless.com CEO Austen Collins 、腾讯云中间件总经理 &amp; 首席架构师 Yunong Xiao 等重磅嘉宾进行相关分享和探讨,本次发布会在腾讯云大学、哔哩哔哩、知乎平台同步直播,近千人观看了本次发布会。</p> <p>产品发布会上,首先,腾讯云中间件总经理&amp;首席架构师 Yunong Xiao 从宏观的角度阐述了 Serverless Framework 的特性,宣布产品正式发布。他指出 Serverless 的核心价值在于聚焦业务,不需关注底层资源 (Focus on outcomes, not Infrastructure),此次产品发布,为开发者提供基于 Full Stack 全栈以及 HTTP API 的一站式解决方案。</p> <p><img alt="" class="b-lazy" data-src="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215028635-162430815.png" data-original="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215028635-162430815.png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>接着,serverless.com CEO Austen Collins 讲述了新版本 Serverless Framework 的价值和目标,他希望能帮企业在仅有很少的开发和运维人员的情况下管理大型的线上系统,并提到 serverless 将和腾讯云一起为中国开发者提供最佳的 serverless 开发者体验以及基础架构。</p> <p><img alt="" class="b-lazy" data-src="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215028960-2076610222.jpg" data-original="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215028960-2076610222.jpg" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>第三位分享嘉宾,来自 serverless.com 中国区研发的负责人 Ke Huang 老师,Ke Huang 老师分享了 Serverless Framework 的演进,从 plugin 到 component 再到 platform,提到未来 serverless 产品路线将提供更完善的 Serverless 组件库生态,提供差量部署,极速部署体验,包含线上运维工具的 Serverless Dashboard,以及上线各种开箱即用的线上运维的工具,让开发者开发完成即部署完成并且已为产线环境准备就绪。</p> <p><img alt="" class="b-lazy" data-src="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215029504-306577443.png" data-original="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215029504-306577443.png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>最后一位分享嘉宾是来自腾讯云的高级产品经理方坤丁 (Tina),Tina 详细介绍了 Serverless Framework 新版本的特性和优化内容,以及在实际业务场景中使用 Serverless Component 的便利。同时,Tina 现场进行了 Serverless Framework 新功能的代码实战,基于 Express.js 框架演示了一键部署、实时日志、远端调试等能力。并通过 Serverless Framework 平台快速部署了一个具备前端、后端和数据库的 Serverless Full Stack 全栈应用。</p> <p><img alt="" class="b-lazy" data-src="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215030342-828853046.png" data-original="https://img2020.cnblogs.com/other/1718416/202004/1718416-20200422215030342-828853046.png" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /></p> <p>发布会嘉宾分享结束后,进行 Q&amp;A 环节,大家就实际业务中遇到的问题在会话区踊跃提问,主持人王俊杰组织嘉宾解答大家的问题,其中有参会者 Jovi 提到「目前 Tencent Serverless 是否支持用户自定义 DB,比如 elasticsearch,couchbase,redis,mongoDB 等其他 NoSQL,未来是否有这方面的支持和拓展计划,以及可能上线的时间点」,可见大家对腾讯云 Serverless Framework 产品以及其未来的发展非常关注。</p> <span id="OSC_h2_1"></span> <h2>立即体验</h2> <p>只需 3 秒!一键部署 <a href="https://serverless.cloud.tencent.com/deploy/express" rel="nofollow">Serverless HTTP</a> 服务:</p> <p><a href="https://serverless.cloud.tencent.com/deploy/express" rel="nofollow">https://serverless.cloud.tencent.com/deploy/express</a></p> <blockquote> <p><strong>传送门:</strong></p> <ul><li>官网:<a href="https://cloud.tencent.com/product/sls" rel="nofollow">Serverless Framework</a></li> <li>GitHub: <a href="https://github.com/serverless/serverless/blob/master/README_CN.md" rel="nofollow">github.com/serverless</a></li> </ul></blockquote> <p>欢迎访问:<a href="https://serverlesscloud.cn/" rel="nofollow">Serverless 中文网</a>,您可以在 <a href="https://serverlesscloud.cn/best-practice" rel="nofollow">最佳实践</a> 里体验更多关于 Serverless 应用的开发!</p> <hr /><blockquote> <p>推荐阅读:<a href="https://item.jd.com/12592747.html?cu=true&amp;utm_source=kong&amp;utm_medium=tuiguang&amp;utm_campaign=t_1001542270_1001895103_0_1912401775&amp;utm_term=536a21c6d22b4b48ba4c98e8045fd352" rel="nofollow">《Serverless 架构:从原理、设计到项目实战》</a></p> </blockquote> <div class="alert alert-success" role="alert"><p>来源:<code>oschina</code></p><p>链接:<code>https://my.oschina.net/u/4305580/blog/3420985</code></p></div></div> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field--label">标签</div> <div class="field--items"> <div class="field--item"><a href="/tag/tina" hreflang="zh-hans">tina</a></div> <div class="field--item"><a href="/tag/github" hreflang="zh-hans">github</a></div> <div class="field--item"><a href="/tag/framework" hreflang="zh-hans">Framework</a></div> <div class="field--item"><a href="/tag/serverless" hreflang="zh-hans">Serverless</a></div> <div class="field--item"><a href="/tag/elasticsearch" hreflang="zh-hans">ElasticSearch</a></div> <div class="field--item"><a href="/tag/mongodb" hreflang="zh-hans">mongodb</a></div> <div class="field--item"><a href="/tag/expressjs" hreflang="zh-hans">Express.js</a></div> <div class="field--item"><a href="/tag/redis" hreflang="zh-hans">Redis</a></div> <div class="field--item"><a href="/tag/deploy" hreflang="zh-hans">Deploy</a></div> <div class="field--item"><a href="/tag/nosql" hreflang="zh-hans">nosql</a></div> </div> </div> Thu, 23 Apr 2020 02:27:57 +0000 亡梦爱人 3588827 at https://www.e-learn.cn