Class handler for a route.

Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method GET, POST, PUT or DELETE (other methods are not suported yet).

Each route can have one or more handler methods, which are executed when the route is matched.

Route handlers definition takes the following form:

class MyHandler
    super Handler

    redef fun METHOD(req, res) do end
end

Where:

  • MyHandler is the name of the handler you will add to the app.
  • METHOD can be replaced by get, post, put or delete.

The following example responds with Hello World! to GET and POST requests:

class MyHandler
    super Handler

    redef fun get(req, res) do res.send "Got a GET request"
    redef fun post(req, res) do res.send "Got a POST request"
end

To make your handler responds to a specific route, you have to add it to the app.

Respond to POST request on the root route (/), the application's home page:

var app = new App
app.use("/", new MyHandler)

Respond to a request to the /user route:

app.use("/user", new MyHandler)

Introduced properties

type BODY: Serializable

popcorn :: Handler :: BODY

Kind of objects returned by deserialize_body
fun all(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: all

Handler to all kind of HTTP request methods.
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 post(req: HttpRequest, res: HttpResponse)

popcorn :: Handler :: post

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

popcorn :: Handler :: put

PUT handler.
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

Redefined properties

redef type SELF: Handler

popcorn $ Handler :: 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 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::Handler Handler core::Object Object popcorn::Handler->core::Object popcorn::Handler... ... popcorn::Handler...->popcorn::Handler

Parents

interface Object

core :: Object

The root of the class hierarchy.

Children

class AppHome

popcorn :: AppHome

abstract class AuthHandler

popcorn :: AuthHandler

AuthHandler allows access to session user
class GithubLogin

popcorn :: GithubLogin

Github OAuth login handler.
class GithubLogout

popcorn :: GithubLogout

Destroy user session and redirect to homepage.
class GithubOAuthCallBack

popcorn :: GithubOAuthCallBack

Get the authentification code and translate it to an access token.
class PopLogger

popcorn :: PopLogger

Display log info about request processing.
class RequestClock

popcorn :: RequestClock

Initialize a clock for the resquest.
class Router

popcorn :: Router

Mountable routers
class SessionInit

popcorn :: SessionInit

Initialize session in request if non existent.
class StaticHandler

popcorn :: StaticHandler

Static files server.
abstract class TrackerHandler

popcorn :: TrackerHandler

Base tracker handler

Descendants

class App

popcorn :: App

Popcorn application.
class GithubUser

popcorn :: GithubUser

Get the currently logged in user from session.
class PopTracker

popcorn :: PopTracker

Saves logs into a MongoDB collection
class PopTrackerAPI

popcorn :: PopTrackerAPI

JSON API of the PopTracker
class PopTrackerBrowsers

popcorn :: PopTrackerBrowsers

Group and count entries by browser
class PopTrackerEntries

popcorn :: PopTrackerEntries

List all tracker log entries
class PopTrackerQueries

popcorn :: PopTrackerQueries

Group and count entries by query string
class PopTrackerResponseTime

popcorn :: PopTrackerResponseTime

Return last month response time

Class definitions

popcorn $ Handler
# Class handler for a route.
#
# **Routing** refers to determining how an application responds to a client request
# to a particular endpoint, which is a URI (or path) and a specific HTTP request
# method GET, POST, PUT or DELETE (other methods are not suported yet).
#
# Each route can have one or more handler methods, which are executed when the route is matched.
#
# Route handlers definition takes the following form:
#
# ~~~nitish
# class MyHandler
#	super Handler
#
#	redef fun METHOD(req, res) do end
# end
# ~~~
#
# Where:
# * `MyHandler` is the name of the handler you will add to the app.
# * `METHOD` can be replaced by `get`, `post`, `put` or `delete`.
#
# The following example responds with `Hello World!` to GET and POST requests:
#
# ~~~
# class MyHandler
#	super Handler
#
#	redef fun get(req, res) do res.send "Got a GET request"
#	redef fun post(req, res) do res.send "Got a POST request"
# end
# ~~~
#
# To make your handler responds to a specific route, you have to add it to the app.
#
# Respond to POST request on the root route (`/`), the application's home page:
#
# ~~~
# var app = new App
# app.use("/", new MyHandler)
# ~~~
#
# Respond to a request to the `/user` route:
#
# ~~~
# app.use("/user", new MyHandler)
# ~~~
abstract class Handler

	# Call `all(req, res)` if `route` matches `uri`.
	private fun handle(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
		if route.match(uri) then
			if route isa AppParamRoute then
				req.uri_params = route.parse_uri_parameters(uri)
			end
			all(req, res)
		end
	end

	# Handler to all kind of HTTP request methods.
	#
	# `all` is a special request handler, which is not derived from any
	# HTTP method. This method is used to respond at a path for all request methods.
	#
	# In the following example, the handler will be executed for requests to "/user"
	# whether you are using GET, POST, PUT, DELETE, or any other HTTP request method.
	#
	# ~~~
	# class AllHandler
	#	super Handler
	#
	#	redef fun all(req, res) do res.send "Every request to the homepage"
	# end
	# ~~~
	#
	# Using the `all` method you can also implement other HTTP request methods.
	#
	# ~~~
	# class MergeHandler
	#	super Handler
	#
	#	redef fun all(req, res) do
	#		if req.method == "MERGE" then
	#			# handle that method
	#		else super # keep handle GET, POST, PUT and DELETE methods
	#	end
	# end
	# ~~~
	fun all(req: HttpRequest, res: HttpResponse) do
		if req.method == "GET" then
			get(req, res)
		else if req.method == "POST" then
			post(req, res)
		else if req.method == "PUT" then
			put(req, res)
		else if req.method == "DELETE" then
			delete(req, res)
		else
			res.status_code = 405
		end
	end

	# GET handler.
	#
	# Exemple of route responding to GET requests.
	# ~~~
	# class GetHandler
	#	super Handler
	#
	#	redef fun get(req, res) do res.send "GETrequest received"
	# end
	# ~~~
	fun get(req: HttpRequest, res: HttpResponse) do end

	# POST handler.
	#
	# Exemple of route responding to POST requests.
	# ~~~
	# class PostHandler
	#	super Handler
	#
	#	redef fun post(req, res) do res.send "POST request received"
	# end
	# ~~~
	fun post(req: HttpRequest, res: HttpResponse) do end

	# PUT handler.
	#
	# Exemple of route responding to PUT requests.
	# ~~~
	# class PutHandler
	#	super Handler
	#
	#	redef fun put(req, res) do res.send "PUT request received"
	# end
	# ~~~
	fun put(req: HttpRequest, res: HttpResponse) do end

	# DELETE handler.
	#
	# Exemple of route responding to PUT requests.
	# ~~~
	# class DeleteHandler
	#	super Handler
	#
	#	redef fun delete(req, res) do res.send "DELETE request received"
	# end
	# ~~~
	fun delete(req: HttpRequest, res: HttpResponse) do end
end
lib/popcorn/pop_handlers.nit:23,1--172,3

popcorn :: pop_json $ Handler
redef class Handler

	# Validator used to check body input
	#
	# Here we use the `pop_validation` module to validate JSON document from the request body.
	var validator: nullable DocumentValidator = null

	# Validate body input with `validator`
	#
	# Try to validate the request body as a json document using `validator`:
	# * Returns the validated string input if the result of the validation is ok.
	# * Answers a json error and returns `null` if something went wrong.
	# * If no `validator` is set, returns the body without validation.
	#
	# Example:
	#
	# ~~~nit
	# class ValidatedHandler
	#	super Handler
	#
	#	redef var validator = new MyObjectValidator
	#
	#	redef fun post(req, res) do
	#		var body = validate_body(req, res)
	#		if body == null then return # Validation error
	#		# At this point popcorn returned a HTTP 400 code with the validation error
	#		# if the validation failed.
	#
	#		# TODO do something with the input
	#		print body
	#	end
	# end
	#
	# class MyObjectValidator
	#	super ObjectValidator
	#
	#	init do
	#		add new StringField("name", min_size=1, max_size=255)
	#	end
	# end
	# ~~~
	fun validate_body(req: HttpRequest, res: HttpResponse): nullable String do
		var body = req.body

		var validator = self.validator
		if validator == null then return body

		if not validator.validate(body) then
			res.json(validator.validation, 400)
			return null
		end
		return body
	end

	# Deserialize the request body
	#
	# Returns the deserialized request body body or `null` if something went wrong.
	# If the object cannot be deserialized, answers with a HTTP 400.
	#
	# See `BODY` and `new_body_object`.
	#
	# Example:
	# ~~~nit
	# class MyDeserializedHandler
	#	super Handler
	#
	#	redef type BODY: MyObject
	#
	#	redef fun post(req, res) do
	#		var form = deserialize_body(req, res)
	#		if form == null then return # Deserialization error
	#		# At this point popcorn returned a HTTP 400 code if something was wrong with
	#		# the deserialization process
	#
	#		# TODO do something with the input
	#		print form.name
	#	end
	# end
	#
	# class MyObject
	#	serialize
	#
	#	var name: String
	# end
	# ~~~
	fun deserialize_body(req: HttpRequest, res: HttpResponse): nullable BODY do
		var body = req.body
		var deserializer = new JsonDeserializer(body)
		var form = deserializer.deserialize(body_type)
		if not form isa BODY or deserializer.errors.not_empty then
			res.json_error("Bad input", 400)
			return null
		end
		return form
	end

	# Kind of objects returned by `deserialize_body`
	#
	# Define it in each sub handlers depending on the kind of objects sent in request bodies.
	type BODY: Serializable

	private var body_type: String is lazy do return (new GetName[BODY]).to_s
end
lib/popcorn/pop_json.nit:63,1--165,3