24.4Handler and Filter
So far, our REST service did not interact much with the existing Seaside todo application (other than through the shared model). Often it is however desired to have both — the application and the REST services — served from the same URL.
To achieve this we have to subclass WARestfulFilter
instead of WARestfulHandler
. The WARestfulFilter
simply wraps a Seaside application. That is, it handles REST requests exactly as the WARestfulHandler
, but it can also delegate to the wrapped Seaside application.
To update our existing service we rename ToDoHandler
to ToDoFilter
and change its superclass to WARestfulFilter
. Now the class definition should look like:
WARestfulFilter subclass: #ToDoFilter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'ToDo-REST'
A filter cannot be registered as a an independent entry point anymore, thus we should remove it from the dispatcher to avoid errors:
WAAdmin unregister: 'todo-api'
Instead we attach the filter the todo application itself. On the class-side of ToDoListView
we adapt the initialize
method to:
ToDoListView class>>initialize
(WAAdmin register: self asApplicationAt: 'todo')
addFilter: ToDoFilter new
After evaluating the initialization code, the ToDoFilter
is now executed whenever somebody accesses our application. The process is visualized in Figure 157. Whenever a request hits the filter (1), it processes the annotations (2). Eventually, if none of the annotated methods matched, it delegates to the wrapped application by invoking the method noRouteFound: (3).
Unfortunately — if you followed the creation of the REST API in the previous sections — our #list
service hides the application by consuming all requests to http://localhost:8080/todo. We have two possibilities to fix the problem:
- We remove the method
#list
so thatToDoFilter
automatically callsnoRouteFound:
that eventually calls the application. - We add a new service that captures requests directed at our web application and explicitly dispatches them to the Seaside application. For example, the following code triggers the wrapped application whenever HTML is requested:
ToDoFilter>>app
<get>
<produces: 'text/html'>
^ self noRouteFound: self requestContext
This change leaves the existing API intact and lets users access our web application with their favorite web browser. This works, because browser request documents with the mime-type text/html
by default. Of course, we can combine this technique with any other of the matching techniques we discussed in the previous chapters.