Mountable routers

Use the Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.

The following example creates a router as a module, loads a middleware handler in it, defines some routes, and mounts the router module on a path in the main app.

class AppHome
    super Handler

    redef fun get(req, res) do res.send "Site Home"
end

class UserLogger
    super Handler

    redef fun all(req, res) do print "User logged"
end

class UserHome
    super Handler

    redef fun get(req, res) do res.send "User Home"
end

class UserProfile
    super Handler

    redef fun get(req, res) do res.send "User Profile"
end

var user_router = new Router
user_router.use("/*", new UserLogger)
user_router.use("/", new UserHome)
user_router.use("/profile", new UserProfile)

var app = new App
app.use("/", new AppHome)
app.use("/user", user_router)

The app will now be able to handle requests to /user and /user/profile, as well as call the Time middleware handler that is specific to the route.

Introduced properties

fun use(path: String, handler: Handler)

popcorn :: Router :: use

Register a handler for a route path.
fun use_after(path: String, handler: Handler)

popcorn :: Router :: use_after

Register a post-handler for a route path.
fun use_before(path: String, handler: Handler)

popcorn :: Router :: use_before

Register a pre-handler for a route path.

Redefined properties

redef type SELF: Router

popcorn $ Router :: SELF

Type of this instance, automatically specialized in every class

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type BODY: Serializable

popcorn :: Handler :: BODY

Kind of objects returned by deserialize_body
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
fun all(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: all

Handler to all kind of HTTP request methods.
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun delete(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: delete

DELETE handler.
fun deserialize_body(req: HttpRequest, res: HttpResponse): nullable BODY

popcorn :: Handler :: deserialize_body

Deserialize the request body
fun get(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: get

GET handler.
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun post(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: post

POST handler.
fun put(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: put

PUT handler.
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
fun use(path: String, handler: Handler)

popcorn :: Router :: use

Register a handler for a route path.
fun use_after(path: String, handler: Handler)

popcorn :: Router :: use_after

Register a post-handler for a route path.
fun use_before(path: String, handler: Handler)

popcorn :: Router :: use_before

Register a pre-handler for a route path.
fun validate_body(req: HttpRequest, res: HttpResponse): nullable String

popcorn :: Handler :: validate_body

Validate body input with validator
fun validator: nullable DocumentValidator

popcorn :: Handler :: validator

Validator used to check body input
protected fun validator=(validator: nullable DocumentValidator)

popcorn :: Handler :: validator=

Validator used to check body input
package_diagram popcorn::Router Router popcorn::Handler Handler popcorn::Router->popcorn::Handler core::Object Object popcorn::Handler->core::Object ...core::Object ... ...core::Object->core::Object popcorn::App App popcorn::App->popcorn::Router popcorn::PopTrackerAPI PopTrackerAPI popcorn::PopTrackerAPI->popcorn::Router

Ancestors

interface Object

core :: Object

The root of the class hierarchy.

Parents

abstract class Handler

popcorn :: Handler

Class handler for a route.

Children

class App

popcorn :: App

Popcorn application.
class PopTrackerAPI

popcorn :: PopTrackerAPI

JSON API of the PopTracker

Class definitions

popcorn $ Router
# Mountable routers
#
# Use the `Router` class to create modular, mountable route handlers.
# A Router instance is a complete middleware and routing system; for this reason,
# it is often referred to as a “mini-app”.
#
# The following example creates a router as a module, loads a middleware handler in it,
# defines some routes, and mounts the router module on a path in the main app.
#
# ~~~
# class AppHome
#	super Handler
#
#	redef fun get(req, res) do res.send "Site Home"
# end
#
# class UserLogger
#	super Handler
#
#	redef fun all(req, res) do print "User logged"
# end
#
# class UserHome
#	super Handler
#
#	redef fun get(req, res) do res.send "User Home"
# end
#
# class UserProfile
#	super Handler
#
#	redef fun get(req, res) do res.send "User Profile"
# end
#
# var user_router = new Router
# user_router.use("/*", new UserLogger)
# user_router.use("/", new UserHome)
# user_router.use("/profile", new UserProfile)
#
# var app = new App
# app.use("/", new AppHome)
# app.use("/user", user_router)
# ~~~
#
# The app will now be able to handle requests to /user and /user/profile, as well
# as call the `Time` middleware handler that is specific to the route.
class Router
	super Handler

	# List of handlers to match with requests.
	private var handlers = new Map[AppRoute, Handler]

	# List of handlers to match before every other.
	private var pre_handlers = new Map[AppRoute, Handler]

	# List of handlers to match after every other.
	private var post_handlers = new Map[AppRoute, Handler]

	# Register a `handler` for a route `path`.
	#
	# Route paths are matched in registration order.
	fun use(path: String, handler: Handler) do
		var route = build_route(handler, path)
		handlers[route] = handler
	end

	# Register a pre-handler for a route `path`.
	#
	# Prehandlers are matched before every other handlers in registrastion order.
	fun use_before(path: String, handler: Handler) do
		var route = build_route(handler, path)
		pre_handlers[route] = handler
	end

	# Register a post-handler for a route `path`.
	#
	# Posthandlers are matched after every other handlers in registrastion order.
	fun use_after(path: String, handler: Handler) do
		var route = build_route(handler, path)
		post_handlers[route] = handler
	end

	redef fun handle(route, uri, req, res) do
		if not route.match(uri) then return
		handle_pre(route, uri, req, res)
		handle_in(route, uri, req, res)
		handle_post(route, uri, req, res)
	end

	private fun handle_pre(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
		for hroute, handler in pre_handlers do
			handler.handle(hroute, route.uri_root(uri), req, res)
		end
	end

	private fun handle_in(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
		for hroute, handler in handlers do
			handler.handle(hroute, route.uri_root(uri), req, res)
			if res.sent then break
		end
	end

	private fun handle_post(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
		for hroute, handler in post_handlers do
			handler.handle(hroute, route.uri_root(uri), req, res)
		end
	end

	private fun build_route(handler: Handler, path: String): AppRoute do
		if handler isa Router or handler isa StaticHandler then
			return new AppGlobRoute(path)
		else if path.has_suffix("*") then
			return new AppGlobRoute(path)
		else
			return new AppParamRoute(path)
		end
	end
end
lib/popcorn/pop_handlers.nit:264,1--381,3