Working with Controllers
Request response cycle.
When web application receives request, dispatcher uses uri and HTTP method to match route for the request. If route is found, associated controller will be called, otherwise it raises ERouteHandlerNotFound
exception. Controller fetches required data from model and use view to compose presentation. Model may retrieve data from storage such as database system. View builds response output to controller which pass response back to web server and eventually back to client browser.
In Fano Framework, controller is any class implements IRequestHandler
interface. This is where application business logic resides.
IRequestHandler interface
IRequestHandler
interface is basis of request handler implementation in Fano Framework. It consists of handleRequest()
method that implementor class must provide. Dispatcher invokes this method and passes request, response and route argument objects. It must return instance of response. You can return response given by dispatcher or return entirely new response instance.
IRequestHandler = interface
['{483E0FAB-E1E6-4B8C-B193-F8615E039369}']
(*!-------------------------------------------
* handle request
*--------------------------------------------
* @param request object represent current request
* @param response object represent current response
* @param args object represent current route arguments
* @return new response
*--------------------------------------------*)
function handleRequest(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
end;
request
, current request objectresponse
, response objectargs
, current route arguments. Read Working with Router for more information about purpose of this object.
Built-in IRequestHandler implementation
Fano Framework provides TAbstractController
, TController
,
TMethodRequestHandler
and TFuncRequestHandler
class. They implement IRequestHandler
interface.
TAbstractController
TAbstractController
is an abstract class. You need to derive and implements its handleRequest()
method to be able to use it. View TAbstractController source code.
unit MyController;
interface
{$MODE OBJFPC}
{$H+}
uses
fano;
type
TMyController = class(TAbstractController)
public
function handleRequest(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse; override;
end;
implementation
function TMyController.handleRequest(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
response.body().write('<html>');
response.body().write('<head><title>My Controller</title></head>');
response.body().write('<body><h1>My Controller</h1></body>');
response.body().write('</html>');
result := response;
end;
end.
TController
TController
is concrete class. It extends TAbstractController
capability by adding view and view parameters to allow, for example, to use template. View TController source code.
TMethodRequestHandler
TMethodRequestHandler
is concrete class. It allows use of any class method as request handler as long as it matches THandlerMethod
. View TMethodRequestHandler source code.
TFuncRequestHandler
TFuncRequestHandler
is concrete class. It allows use of Pascal function as request handler as long as it matches THandlerFunc
. View TFuncRequestHandler source code.
Using TController class
TController
class is built-in class that provides ability for route handler to works with view and view parameters.
Except for simple route handler which display static view, very likely, you need to extend this class. TController
by default, just calls render()
method of IView
, which is
returning response from IView
instance.
Creating TController class
TController
constructor expects 2 parameters
constructor TController.create(
const viewInst : IView;
const viewParamsInst : IViewParameters
);
viewInst
, view to be used, i.e., instance of class that implementsIView
interface.ViewParamsInst
, view parameters, i.e., instance of class that implementsIViewParameters
interface.
viewInst
and viewParamsInst
that you pass during class construction, will be available from inherited class as fView
and fViewParams
protected fields, respectively.
For more information regarding view and view parameters, read Working with Views.
Implements controller logic
handleRequest()
is method that will be invoked by dispatcher to handle request, so you mostly do not call it directly.
This method is part of IRequestHandler
interface. Dispatcher will pass request and response instance to this method.
TController
class provides basic implementation of this method, which is, to return view output. Fano Framework provides some built-in response class that you can use such HTML response, JSON response or binary response (for example to output image). Of course, you are free to implements your own output response.
Custom IRequestHandler implementation
TAbstractController
and TController
are provided for convinience. In fact, you are not required to use them at all. You can use any class as long as it implements IRequestHandler
interface.
For example, if you prefer to handle CRUD task in a single class instead of multiple class.
unit UserController;
interface
{$MODE OBJFPC}
{$H+}
uses
fano;
type
TUserController = class(TInterfacedObject, IRequestHandler)
public
function createUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
function showUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
function updateUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
function deleteUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
function handleRequest(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse; override;
end;
implementation
function TUserController.createUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
//handle user creation
result := response;
end;
function TUserController.showUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
//handle view user
result := response;
end;
function TUserController.updateUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
//handle user update
result := response;
end;
function TUserController.deleteUser(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
//handle user deletion
result := response;
end;
function TUserController.handleRequest(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
result := response;
case request.method of
'GET' : result := showUser(request, response, args);
'POST' : result := createUser(request, response, args);
'PUT' : result := updateUser(request, response, args);
'DELETE' : result := deleteUser(request, response, args);
end;
end;
end.
You register route as follows
router.any('/users', TUserController.create());
Use class method or function as request handler
Fano Framework provides TMethodRequestHandler
and TFuncRequestHandler
adapter class which implements IRequestHandler
interface to allow use of method or function as request handler. For example, using TUserController
class above,
var
userCtrl : TUserController;
router.get(
'/users/show/{id}',
TMethodRequestHandler.create(@userCtrl.showUser)
);
router.post(
'/users/create',
TMethodRequestHandler.create(@userCtrl.createUser)
);
router.put(
'/users/update',
TMethodRequestHandler.create(@userCtrl.updateUser)
);
You can also use ordinary function,
function hello(
const request : IRequest;
const response : IResponse;
const args : IRouteArgsReader
) : IResponse;
begin
//handle view user
result := response;
end;
router.get(
'/users/show/{id}',
TFuncRequestHandler.create(@hello)
);
TFuncRequestHandler video tutorial explains how to use this class.