Mar*_*oni 4 javascript functional-programming functional-testing node.js express
我的目标是能够为Express.js服务器编写纯路由.这甚至可能吗?
为了访问数据库和我知道的东西,我可以使用神话般的Futuremonad保持纯净,但路线渲染本身怎么样?
我发现的最大困难之一是路线可能以不同的方式结束,例如:
使用Futuremonad,我可以处理错误和成功案例,但之后的成功案例没有更多的粒度.
有没有办法为Express.js编写纯粹且完全可测试的路由?
简答:不 - 这是不可能的.
说明:在函数式编程的上下文中,我们有一个数据流 - 程序接受一些输入数据,转换并返回输出数据.
在服务器的情况下,我们有两个数据流.首先是当你启动服务器时.在这个流程中,您可能希望从外部世界读取配置文件或命令行参数,例如端口,主机,数据库字符串等.这是一个副作用,因此我们通常将它放在Future中.例,
readJson(process.argv[2]) // Read configuration file
.chain(app) // Get app instance (routes, middlewares, etc.)
.chain(start) // Start server
.run().promise()
.then((server) => info(`Server running at: ${server.info.uri}`))
.catch(error);
Run Code Online (Sandbox Code Playgroud)
这是典型的index.js文件,包含所有副作用(读取配置).现在让我们转向第二个数据流.
这个数据流有点难以想象.第一个数据流的输出/副作用是服务器侦听某个端口以进行外部连接.现在想象一下,每个请求作为一个独立的数据流来到这个服务器本身.
就像index.js是用于处理所有副作用的文件一样,您的路由文件或路由处理函数用于处理副作用,即结果请求应该在此路由处理程序中回复.通常,接近纯粹的功能路线看起来像:
function handler(request, reply) {
compose(serveFile(reply), fileToServe)(request)
.orElse((err) => err.code === 'ENOENT' ? reply404(reply) : reply500(reply))
.run(); // .run() is the side effect
}
return {
method: 'GET',
path: '/employer/{files*}',
handler
};
Run Code Online (Sandbox Code Playgroud)
在上面的片段中,一切都是纯粹的.只有不纯的东西是.run()方法(我正在使用Hapi.js和Folktale.js任务).
与Angular或React等前端框架相同的想法也是如此.这些框架中的组件应包含所有影响/杂质.像路由处理程序一样,这些组件是应该发生副作用的端点.您的模型/服务应该没有杂质.
话虽如此,如果你仍然希望让你的路线完全纯净,那么就有希望.你最本想做的是 - 更高层次的抽象:
对于前端框架,如前所述,您的UI组件将具有副作用.但是有一些新的框架超越了这种抽象.Cycle.js是我所知道的一个框架.它应用比Angular或React更高级别的抽象.所有的副作用都被观察到了,然后被分派到一个框架并执行所有(我的字面意思是100%)你的组件纯粹.
我试图使用Hapi.js + Folktale.js + Ramda创建服务器.我的想法最初追溯到Cycle.js.但是,我取得了轻微的成功.有些部分代码非常难以编写,而且限制性太强.之后,我放弃了纯粹的路线.但是,我的其余代码是纯粹的,并且非常易读.
最后,功能编程是关于部分编码而不是整体编码.你或我想要做的是整个函数式编程.至少在JavaScript中,这会让人觉得有点尴尬.
我也不认为你可以用 Express.js 做到这一点,但我可以建议一个替代方案。
Web 服务器可以描述为一个接受Request并提供 的函数Response。但是,如果您检查 Express.js 中发生的情况,您实际上会看到签名实际上是:
(IncomingMessage, ServerResponse) -> ()
Run Code Online (Sandbox Code Playgroud)
例如:
const express = require('express')
const handler = (req, res, next) =>
doSomethingAsync(req.body).then(res.json).catch(next)
express()
.get('/', handler)
.listen(3000, console.error)
Run Code Online (Sandbox Code Playgroud)
虽然我们更喜欢这样的东西:
Request -> Promise Response
Run Code Online (Sandbox Code Playgroud)
Paperplane是一个轻量级 NodeJS Web 服务器框架,适用于上述类型的签名。这个想法是使用纯函数来转换您想要发送的响应。当谈到路由时,它有两个允许干净声明的函数:
routes :: { k: (Request -> Promise Response) } -> (Request -> Response)
Run Code Online (Sandbox Code Playgroud)
和
methods :: { k: (Request -> Promise Response) } -> (Request -> Promise Response)
Run Code Online (Sandbox Code Playgroud)
从Paperplane 的 API 文档中查看这个简单的示例:
const http = require('http')
const { mount, routes } = require('paperplane')
const { fetchUser, fetchUsers, updateUser } = require('./lib/users')
const app = routes({
'/users': methods({
GET: fetchUsers
}),
'/users/:id': methods({
GET: fetchUser,
PUT: updateUser
})
})
http.createServer(mount({ app })).listen(3000)
Run Code Online (Sandbox Code Playgroud)
它还提供其他功能来解决您遇到的困难。
另外,它还支持代数数据类型 (ADT),您可以从可爱的Crocks库中获取它。
| 归档时间: |
|
| 查看次数: |
1527 次 |
| 最近记录: |