Fullmoon是一个基于Redbean的快速而简约的web框架,Redbean是一个可移植的、单文件可分发的web服务器。
您需要的一切都在一个文件中,没有运行在Windows、Linux和ormacOS上的外部依赖项(用于开发和分发)。以下是Fullmoon应用程序的完整示例:
本地fm=require";满月";调频。setTemplate(";你好";,";你好,{%&;name%}";)调频。setRoute(";/hello/:name";,函数(r)返回fm。serveContent(";hello";,{name=r.params.name})结束)fm。跑()
之后';它是用红豆包装的,可以发射/红豆。com,它启动一个返回";你好,世界";发送到的HTTP(S)请求http://localhost:8080/hello/world.
Redbean是一个单文件可分发的跨平台web服务器,具有独特而强大的特性。虽然有几种基于Lua的Web框架(Lapis、Lor、Seall、Pegasus等),但它们都没有与Redbean集成(尽管有一个实验框架anpan)。
Fullmoon是一个轻量级的、极简的web框架,它通过以最简单、最高效的方式扩展和增强RedBean提供的所有功能来展示。它运行速度快,自带电池(路由、模板、JSON生成等)。
Fullmoon遵循Lua的理念,提供了一套最少的工具,可以根据需要进行组合,并作为构建的基础。
您可以通过运行以下命令下载Redbean的副本(如果您在Windows上,请跳过第二个命令):
将代码保存到名为的文件中。初始化。lua(例如,描述中显示的Luacode)。
另一个选项是将框架代码放在一个单独的文件中(例如,.lua/myapp.lua),并添加require";myapp";到初始化。卢阿。
如果您的框架代码存储在单独的Lua文件中,请确保将其放入。lua/文件夹并将该文件压缩。
将浏览器指向http://127.0.0.1:8080/hello/world你应该看到";你好,世界";(假设您使用的代码如简介或用法部分所示)。
最简单的应用程序需要加载模块、配置oneroute并运行应用程序:
此应用程序将返回";你好,世界";内容(和200 HTTP状态),并对所有其他请求返回404状态。
setRoute(route[,action]):注册一条路由。如果route是一个字符串,那么它将被用作一个route表达式来比较请求路径和。如果它是一个表,那么它的元素是用作路由的字符串,它的哈希值是路由检查的条件。如果第二个参数是函数,则在满足所有条件时执行。如果它是一个字符串,那么它将用作路由表达式,并且处理请求的方式就像在指定的路由上发送请求一样(充当内部重定向)。如果任何条件不满足,则检查下一条路线。路由表达式可以有多个参数和可选部分。操作处理程序acceptsa请求表,提供对请求和routeparameters以及头和cookie的访问。
setTemplate(名称、模板):使用指定的名称注册模板。如果模板是字符串,那么它是';它被编译成一个模板处理程序。如果它是一个函数,则在请求呈现模板时存储并调用它。如果是';s是一个表,然后它的第一个元素是一个模板或一个函数,其余元素用作选项。例如,将ContentType指定为选项之一将为生成的内容设置ContentTypeHeader。默认情况下提供两个模板(500和json),可以覆盖。
ServerResponse(status[,headers][,body]):使用提供的status、headers和body值发送HTTP响应。headers是一个可选表,由HTTP头名称/值对填充。如果提供了此标题集,则在处理同一请求期间,此标题集会删除之前的所有其他标题集。标头名称不区分大小写,但为带有破折号的标头名称提供的别名区分大小写:{ContentType=";foo";}是{[";内容类型";]="foo";}。body是可选字符串。
serveContent(名称、参数):使用提供的参数呈现模板。name是命名模板的字符串(由setTemplatecall设置),parameters是带有模板参数的表(模板中引用为变量)。
运行([options]):使用配置的路由运行服务器。默认情况下,服务器监听本地主机和端口8080。可以通过在选项表中设置addr和port值来更改这些值。
所有的例子都包括一个独立的模块(一个.lua文件),它需要被复制到/。lua文件夹(以及fullmoon.lua文件)。运行示例要求在中包含require语句。初始化。lua文件,加载模块,因此对于showcase示例。初始化。lua包括以下内容:
TechEmpower示例使用Fullmoon和内存中的sqlite数据库实现web框架基准测试的各种测试类型。
HTMXBoard示例演示了一个简单的fullmoon应用程序,该应用程序使用htmx库生成交付给客户机的TML片段。
注1:由于所有数据都存储在内存中,因此本例要求运行带有-u(uniprocess)选项的runningredbean。
注2:本示例从外部资源检索htmx、hyperscript和可排序库,但这些库也可以存储为本地资产,从而提供一个完全自给自足的便携分发包。
获取路径URL,并按照注册路由的顺序将其与每个路由URL进行匹配
为满足所有条件的对象调用指定的操作处理程序(传递请求表)
如果操作处理程序返回的不是false或nil,则服务于响应(否则继续该过程)
通常,路由定义将请求URL(和一组条件)绑定到操作处理程序(这是常规的Lua函数)。对于与路由定义匹配的每个URL,都会以随机顺序检查所有条件。一旦任何条件失败,路由处理将中止,并检查下一条路由,但有一个例外:任何条件都可以设置“否则”值,这将触发具有指定状态的响应。
如果没有与请求匹配的路由,则启动默认的404处理,可以通过注册自定义的404模板(fm.setTemplate(";404";,";My 404 page…";)来定制。
每条路线都有一条完全匹配的路径,因此路线"/你好";匹配请求/你好和不';t match/hello world或/hello/world。要匹配/hello只是其中一部分的路径,可以使用可选参数和splat)。
此应用程序的响应为";你好,世界" 对于在/hello路径上定向的所有请求,返回404,用于所有其他请求。
除了固定路由之外,任何路径都可能包含参数的占位符,这些占位符后面紧跟着参数名,后面是:来标识:
每个参数匹配一个或多个字符,除了/,因此路由"/你好/:name";匹配/hello/alice、/hello/bob、/hello/123和不匹配/hello/bob/和/alice(因为不匹配的正斜杠)或/hello/(因为要匹配的片段的长度为零)。
可以使用请求表及其paramstable访问参数,例如r.params。name可用于从前面的示例中获取name参数的值。
还有另一种称为splat的参数,它写为*并匹配零个或多个字符,包括正斜杠(/)。splat还存储在params表中的splat名称下。例如,路线"/下载/*";匹配/下载/我的/文件。zipand splat获取my/file的值。拉链如果同一路线中需要多个splat,则可以为splat分配类似于其他参数的名称:/download/*path/*fname。zip(尽管使用/download/*path/:fname.zip可以获得相同的结果,因为第一个splati将捕获除文件名以外的所有路径部分)。
所有参数(包括splat)都可以出现在路径的任何部分,并且可以被其他文本包围,这些文本需要精确匹配。这意味着路线"/下载/*/:姓名:分机";匹配/download/my/path/file。zip和params。name获取文件,params。ext获得zip和params。splat获取my/path值。
任何指定的路由片段或参数都可以通过将其括在括号中声明为可选:
在上面的示例中,/hello和/hello/Bob都将被接受,但不接受/hello/,因为尾部斜杠是可选片段的一部分,并且:name仍然需要一个或多个字符。
任何不匹配的可选参数的值都为false,因此在上述情况下";你好,世界" 为/hello请求URL返回。
可以指定多个可选参数,并且可以嵌套optionalfragments,因此"/帖子(/:pid/评论(/:cid))和#34;和"/帖子(/:pid)/评论(/:cid)和#34;是有效的路由值。
参数的默认值是一个或多个长度的所有字符(除了/)。要指定一组不同的有效字符,可以将其添加到变量名的末尾;例如,使用:id[%d]而不是:id将参数更改为仅匹配数字。
支持以下Lua字符类:%w、%d、%a、%l、%u和%x;任何标点符号(包括%和])也可以用%转义。不支持负类(以Lua的形式写成%W),但不支持set语法,因此[^%d]匹配的参数不';不包括任何数字。
注意重复的次数可以是';不能更改(因此:id[%d]*不是接受零或多个数字的有效方法),因为只允许设置,并且值仍然接受一个或多个字符。如果需要更灵活地描述可接受的格式,那么可以使用CustomValidator来扩展匹配逻辑。
查询和表单参数的访问方式与使用请求表中的params表的pathparameters相同,请求表已传递给每个操作处理程序。请注意,如果参数和查询/表单名称之间存在冲突,则参数名称优先。
尽管之前所有的例子都显示了一条路线,但它';在实际应用中很少出现这种情况;当存在多条路由时,它们总是按照注册顺序进行评估。
当多个路由具有相同的条件集并共享相同的操作处理程序时,一个setRoute调用还可以设置多个路由:
考虑到路由是按照设置的顺序进行评估的,因此需要首先设置更多选择性路由,否则它们可能无法获得评估的机会:
如果按相反的顺序设置路由,/user/bob可能永远不会被检查,只要"/user/:name";操作处理程序返回一些非错误的结果。
每个路由都可以提供一个可选名称,当需要基于特定参数值生成其URL时,该名称在引用该路由时非常有用。提供的makePath函数接受路由名称或路由URL本身以及参数表,并使用填充的参数占位符返回路径:
调频。setRoute(";/user/:name";,handlerName)fm。setRoute({";/post/:id";,routeName=";post";},handlerPost)fm。makePath(";/user/:name";,{name=";Bob";})-->/用户/Bobfm。makePath(";/post/:id";,{id=123})-->/邮政/123fm。makePath(";post";,{id=123})-->/第123页,与前一页相同
如果两条路由使用相同的名称,则该名称与上次注册的一条路由关联,但两条路由仍然存在。
路由名称还可以用于仅用于URL生成的外部/静态路由。
如果路由仅用于路径生成,则它不会';甚至不需要有一个路由处理程序:
如果应用程序需要根据请求属性(例如,方法)的特定值执行不同的功能,该库提供了两个主要选项:(1)检查属性值操作处理程序(例如,使用request.method=";GET";check)和(2)添加一个条件,过滤掉请求,以便只有使用指定属性值的请求才能到达操作处理程序。本节将更详细地介绍第二个选项。
默认情况下,每个注册的路由都会响应所有HTTP方法(GET、PUT、POST等),但它';可以将每个路由配置为只响应特定的HTTP方法:
在本例中,语法为fm。获得"/你好(/:name)和#34;将路由配置为仅接受GET请求。此语法相当于通过路由和任何附加过滤条件传递atable:
如果需要指定多个方法,则可以传递带有方法列表的表,而不是一个字符串值:
每个允许GET请求的路由也(隐式)允许HEAD请求,该请求通过返回所有HEADERS而不发送正文本身来处理。如果出于某种原因,这种隐式处理是不可取的,那么将HEAD=false添加到methodtable中会禁用它(如method={";GET";,";POST";,HEAD=false})。
请注意,使用非匹配方法的请求不会';不要被拒绝,而是通过其他路线进行检查,如果没有',则触发返回的404状态;没有匹配(只有一个例外)。
除了方法之外,还可以使用主机、ClientAddress、serverAddr、scheme、请求头和参数应用其他条件。例如,指定name=";鲍勃";其中一个条件是确保name参数的值为";鲍勃";以便调用操作处理程序。
可以使用头名称作为键来检查任何请求头,因此ContentType=";多部分/表格数据";如果内容类型头的值是多部分/表单数据,则满足。请注意,headervalue可能包括其他元素(作为内容类型值的一部分的边界或字符集),并且只比较实际的媒体类型。
字符串值不是条件路由中可以使用的唯一值。如果可以接受多个值,则传递一个表可以提供可接受值的列表。例如,如果Bob和Alicear是可接受的值,那么name={Bob=true,Alice=true}将其表示为一个条件。
表中传递的两个特殊值允许应用正则表达式或模式验证:
正则表达式:接受包含正则表达式的字符串。例如,name={regex=";^(Bob | Alice)$";}与本节前面显示的哈希检查结果相同
模式:接受带有Lua Patren表达式的字符串。例如,name={pattern=";^%u%l+$";}接受以大写字符开头,后跟一个或多个小写字符的值。
这两个检查可以与表存在性检查结合使用:name={Bob=true,regex=";^Alice$";}接受Bob和Alicevalues。如果第一个表存在性检查失败,则返回正则表达式或模式表达式的结果。
自定义验证器的最后一种类型是函数。提供的函数接收要验证的值,其结果被评估为false或true。例如,传递id=tonumber可以确保id值是一个数字。另一个例子是clientAddr=fm。isLoopbackIp确保客户端地址是环回ip地址。
本地函数小于(n)返回函数(l)返回到数字(l)<;n end endfm。setRoute(fm.POST{";/upload";,ContentLength=isLessThan(100000)},函数(r)。。。处理上传。。。(完)
它';请记住,验证器函数实际上会返回一个函数,该函数将在应用检查的请求期间被调用。在上一个示例中,返回的函数接受一个header值,并将其与创建过程中传递的限制进行比较。
在某些情况下,不满足条件是将某些响应返回给客户端而不检查其他路由的充分理由。在这种情况下,将“否则”值设置为数字或函数将返回具有指定状态的响应或函数结果:
本地函数小于(n)返回函数(l)返回到数字(l)<;n end endfm。setRoute(fm.POST{";/upload";,ContentLength=isLessThan(100000),否则=413},函数(r)。。。处理上传。。。(完)
在本例中,路由引擎匹配路由,然后验证两个条件:将方法值与POST进行比较,将内容长度头的值与IslessThen函数的结果进行比较。如果其中一个条件不符合';t match,否则值指定的状态将与响应的其余部分一起返回。
如果返回的状态只需要应用于ContentLength检查,则可以将otherwise值与validator函数一起移动到与ContentLength检查关联的表中:
最后两个示例之间的区别在于,在本例中,只有ContentLength检查失败触发413响应(所有其他方法都会通过其他路由),而在前一个示例中,方法和ContentLength检查失败都会触发相同的413响应。
注意,当checked值为nil时,对表的检查被认为是有效的,路由不会被拒绝。例如,根据字符串(name=";Bo";)检查可选参数如果参数的值为0,则失败。name为nil,但如果对一个表(name={Bo=true,Mo=true})进行了相同的检查,包括正则表达式/模式检查,则会通过。如果不需要这样做,那么customvalidator函数可以显式检查正确的值。
调频。setRoute({";/hello(/:name)";,method={";GET";,";POST";,否则=405}),函数(r)返回";你好"。。(r.params.name或";World!";)(完)
在这种情况下,如果使用PUT方法访问该端点,则不会检查其他路由(因为不满足方法条件),而是返回405状态,如使用指定的“否则”值配置的。如前所述,当GET请求被接受时,该路由也会接受HEAD请求(即使未列出)。
当405(坏方法)状态返回且未设置允许标头时,它被设置为路由允许的方法列表。在上面的案例中,它被设置为GET、POST、HEAD、OPTIONS值,因为这些是此配置允许的方法。如果“否则”值是一个函数(而不是一个数字),则该函数负责返回正确的结果并设置Allow标头。
也可以将“否则”值设置为函数,这比仅设置状态值更灵活。例如,设置否则=fm。服务器响应(413和34;负载太大和34;)触发器响应指定的状态和消息。
操作处理程序接收所有传入的HTTP请求,这些请求经过特定路由过滤。目前显示的每个示例都包含一个actionhandler,它作为第二个参数传递给setRoute方法。
在处理onerequest的过程中可以执行多个操作处理程序,一旦一个处理程序返回一个评估为非假值的结果,路由处理过程就结束。从操作处理程序返回False或nil将继续处理,这允许实现一些应用于多个路由的常见处理(类似于u
......