在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):bungle/lua-resty-route开源软件地址(OpenSource Url):https://github.com/bungle/lua-resty-route开源编程语言(OpenSource Language):Lua 99.8%开源软件介绍(OpenSource Introduction):lua-resty-routelua-resty-route is a URL routing library for OpenResty supporting multiple route matchers, middleware, and HTTP and WebSockets handlers to mention a few of its features. Matchers
Matcher is selected by a prefix in a route's pattern, and they do somewhat
follow the Nginx's
¹ Lua Prefix MatcherPrefix, as the name tells, matches only the prefix of the actual location.
Prefix matcher takes only static string prefixes. If you need anything more
fancy, take a look at regex matcher. Prefix can be matched case-insensitively
by prefixing the prefix with route "/users" (function(self) end) This route matches locations like:
But it doesn't match location paths like:
But those can be still be matched in case-insensitive way: route "*/users" (function(self) end) Equals MatcherThis works the same as the prefix matcher, but with this
we match the exact location, to use this matcher, prefix
the route with route "=/users" {
get = function(self) end
} This route matches only this location:
Case-insensitive variant can be used also: route "=*/users" {
get = function(self) end
} And this of course matches locations like:
Match MatcherThis matcher matches patters using Lua's route "#/files/(%w+)[.](%w+)" {
get = function(self, file, ext) end
} This will match location paths like:
In that case the provided function (that answers only HTTP For many, the regular expressions are more familiar and more powerfull. That is what we will look next. Regex MatcherRegex or regular expressions is a common way to do pattern matching.
OpenResty has support for PCRE compatible regualar expressions, and
this matcher in particular, uses route [[~^/files/(\w+)[.](\w+)$]] {
get = function(self, file, ext) end
} As with the Match matcher example above, the end results are the same and the function will be called with the captures. For Regex matcher we also have case-insensitive version: route [[~*^/files/(\w+)[.](\w+)$]] {
get = function(self, file, ext) end
} Simple MatcherThis matcher is a specialized and limited version of a Regex matcher with one advantage. It handles type conversions automatically, right now it only supports integer conversion to Lua number. For example: route:get "@/users/:number" (function(self, id) end) You could have location path like:
The function above will get Supported simple capturers are:
In future, we may add other capture shortcuts. Of course there is a case-insensitive version for this matcher as well: route:get "@*/users/:number" (function(self, id) end) The simple matcher always matches the location from the beginning to end (partial matches are not considered). RoutingThere are many different ways to define routes in To define routes, you first need a new instance of route. This instance
can be shared with different requests. You may create the routes in
local route = require "resty.route".new() Now that we do have this Note: Routes are tried in the order they are added when dispatched.
This differs from how Nginx itself handles the HTTP RoutingHTTP routing is the most common thing to do in web related routing. That's
why HTTP routing is the default way to route in The most common HTTP request methods (sometimes referred to as verbs) are:
While these are the most common ones, The General Pattern in Routingroute(...)
route:method(...) or route(method, pattern, func)
route:method(pattern, func) e.g.: route("get", "/", function(self) end)
route:get("/", function(self) end) Only the first function argument is mandatory. That's why we can
call these functions in a quite flexible ways. For some Defining Routes as a Tableroute "=/users" {
get = function(self) end,
post = function(self) end
}
local users = {
get = function(self) end,
post = function(self) end
}
route "=/users" (users)
route("=/users", users) Using Lua Packages for Routingroute "=/users" "controllers.users"
route("=/users", "controllers.users") These are same as: route("=/users", require "controllers.users") Defining Multiple Methods at Onceroute { "get", "head" } "=/users" (function(self) end) Defining Multiple Routes at Onceroute {
["/"] = function(self) end,
["=/users"] = {
get = function(self) end,
post = function(self) end
}
} Routing all the HTTP Request Methodsroute "/" (function(self) end)
route("/", function(self) end) The Catch all Routeroute(function(self) end) Going Crazy with Routingroute:as "@home" (function(self) end)
route {
get = {
["=/"] = "@home",
["=/users"] = function(self) end
},
["=/help"] = function(self) end,
[{ "post", "put"}] = {
["=/me"] = function(self)
end
},
["=/you"] = {
[{ "get", "head" }] = function(self) end
},
[{ "/files", "/cache" }] = {
-- requiring controllers.filesystem returns a function
[{"get", "head" }] = "controllers.filesystem"
}
} As you may see this is pretty freaky. But it doesn't actually
stop here. I haven't even mentioned things like callable Lua
tables (aka tables with metamethod WebSockets RoutingFile System RoutingFile system routing is based on a file system tree. This could be considered as a routing by a convention. File system routing depends on either LuaFileSystem module or a preferred and LFS compatible ljsyscall. As an example, let's consider that we do have this kind of file tree:
This file tree will provide you with the following routes:
The files could look like this (just an example):
return {
get = function(self) end,
post = function(self) end
}
return {
get = function(self) end,
post = function(self) end,
delete = function(self) end
}
return function(self) end
return function(self) end
return {
get = function(self, id) end,
put = function(self, id) end,
post = function(self, id) end,
delete = function(self, id) end
}
return {
get = function(self, id) end,
put = function(self, id) end,
post = function(self, id) end,
delete = function(self, id) end
} To define routes based on file system tree you will need to call -- Here we assume that you do have /routing directory
-- on your file system. You may use whatever path you
-- like, absolute or relative.
route:fs "/routing" Using file system routing you can just add new files to file system tree, and they will be added automatically as a routes. Named RoutesYou can define named route handlers, and then reuse them in actual routes. route:as "@home" (function(self) end) (the use of And here we actually attach it to a route: route:get "/" "@home" You can also define multiple named routes in a one go: route:as {
home = function(self) end,
signin = function(self) end,
signout = function(self) end
} or if you want to use prefixes: route:as {
["@home"] = function(self) end,
["@signin"] = function(self) end,
["@signout"] = function(self) end
} Named routes must be defined before referencing them in routes. There are or will be other uses to named routers as well. On todo list there are things like reverse routing and route forwarding to a named route. MiddlewareMiddleware in The most common type of Middleware is request level middleware: route:use(function(self)
-- This code will be run before router:
-- ...
self.yield() -- or coroutine.yield()
-- This code will be run after the router:
-- ...
end) Now, as you were already hinted, you may add filters to specific routes as well: route.filter "=/" (function(self)
-- this middleware will only be called on a specific route
end) You can use the same rules as with routing there, e.g. route.filter:post "middleware.csrf" Of course you can also do things like: route.filter:delete "@/users/:number" (function(self, id)
-- here we can say prevent deleting the user who
-- issued the request or something.
end) All the matching middleware is run on every request, unless one of them
decides to
The order of middleware is by scope:
If there are multiple requet or router level middleware, then they will be executed the same order they were added to a specific scope. Yielded middleware is executed in reverse order. Yielded middleware will only be resumed once. Internally we do use Lua's great We are going to support a bunch of predefined middleware in a future. EventsEvents allow you to register specialized handlers for different HTTP status codes or other predefined event codes. There can be only one handler for each code or code group. You can for example define route:on(404, function(self) end) Some groups are predefined, e.g.:
You may use groups like this: route:on "error" (function(self, code) end) You can also define multiple event handlers in a one go: route:on {
error = function(self, code) end,
success = function(self, code) end,
[302] = function(self) end
} Then there is a generic catch-all event handler: route:on(function(self, code) end) We will find the right event handler in this order:
Only one of these is called per event. It is possible that we will add other handlers in a future where you could hook on. Router APIYou may have seen in previous examples functions get as a first
parameter a While the above so called router.contextThis is really powerful concept here to share data between different routes and functions. Many middleware will be inserted to context. E.g. a redis middleware could add local ok, err = self.redis:set("cat", "tommy") Opening and closing the Redis connection is something that the
middleware does automatically before scenes. It means that you
don't need to initiate or close the connections to Redis server,
but this small router.yield()Is similar to router:redirect(uri, code)Similar to router:exit(uri, code)Similar to router:exec(uri, args)Similar to router:done()Similar to router:abort()This is reserved for router:fail(error, code)If |