templates.docs.requestContext.html Maven / Gradle / Ivy
{#==========================================
Docs : "Request Context"
==========================================#}
    
        
        The request context
    
    
    
        The request context is the
        object associated with the current request that Spincast passes 
        to your matching route handlers. First of all, it allows you to access information
        about the request, and to build the response to send.
    
    
        Those functionalities are provided by simple methods, or by add-ons. What
        we call an add-on is an intermediate class containing a set of methods. 
        In other words, instead of accessing an utility method directly
        from the request context, "context.getLocaleToUse()" for example, you access
        it thought an intermediate add-on: "context.routing().isNotFoundRoute()".
    
    
        
            
                Here are some add-ons and some standalone methods available by default on the request context:
                {% verbatim %}
public void myHandler(IAppRequestContext context) {
    // Access the request information
    String name = context.request().getPathParam("name");
    // Set the response
    context.response().sendPlainText("Hello world");
    // Get information about the routing process and the current route
    boolean isNotFoundRoute = context.routing().isNotFoundRoute();
    // Get/Set request-scoped variables
    String someVariable = context.variables().getAsString("someVariable");
    // Direct access to the Json manager
    IJsonObject jsonObj = context.json().create();
    // Direct access to the XML manager
    IJsonObject jsonObj2 = context.xml().fromXml("<someObj></someObj>");
    // Direct access the guice context
    ISpincastUtils spincastUtils = context.guice().getInstance(ISpincastUtils.class);
    // Direct access to the templating engine
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("name", "Stromgol");
    context.templating().evaluate("Hello {{name}}", params);
    
    // Get the best locale to use given the current request
    Locale localeToUse = context.getLocaleToUse();
    
    // ...
}
{% endverbatim %} 
            
        
    
    
        
        Again, the main job of the request context is to allow the route handlers to deal with the
        request and the response. But it's also a container where various functionalities can be added 
        to help the route handlers do their job! Take the templating() add-on, for example:
    
    
        
            
                {% verbatim %}
public void myRouteHandler(IDefaultRequestContext context) {
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("name", "Stromgol");
    String content = context.templating().evaluate("Hi {{name}}!", params);
    // Do something with the evaluated content...
}{% endverbatim %}
            
        
    
    
        The templating() add-on does not directly manipulate the request or the response.
        But it provides a useful set of methods for the route handlers. 
    
 
     
        If you have experience with Guice, or with dependency injection in general,
        you may have noticed that we could simply inject the ITemplatingEngine
        instance in that controller:
    
    
        
            
                {% verbatim %}
public class AppController {
    private final ITemplatingEngine templatingEngine;
    @Inject
    public AppController(ITemplatingEngine templatingEngine) {
        this.templatingEngine = templatingEngine;
    }
    protected ITemplatingEngine getTemplatingEngine() {
        return this.templatingEngine;
    }
    public void myRouteHandler(IDefaultRequestContext context) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("name", "Stromgol");
        String content = getTemplatingEngine().evaluate("Hi {{name}}!", params);
        // Do something with the evaluated content...
    }
}{% endverbatim %}
            
        
    
    
        Or even get the ITemplatingEngine instance using the guice() add-on: 
    
    
        
            
                {% verbatim %}
public class AppController {
    public void myRouteHandler(IDefaultRequestContext context) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("name", "Stromgol");
        ITemplatingEngine templatingEngine = context.guice().getInstance(ITemplatingEngine.class);
        String content = templatingEngine.evaluate("Hi {{name}}!", params);
        // Do something with the evaluated content...
    }
}{% endverbatim %}
            
        
    
    
        Those three versions lead to the exact same result. Use the version
        you prefer! But, for functionalities that are often used inside route handlers, or that should be
        request scoped, we think extending the request context may be interesting.
    
    
    
        
        Imagine a plugin which job is to manage authentification and autorization. 
        Wouldn't it be nice if this plugin could add some extra functionalities to the request context? 
        For example :   
    
  
    
        
            
                
public void myHandler(IAppRequestContext context) {
    if(context.auth().isAuthenticated()) {
        String username = context.auth().user().getUsername();
        // ...
    }
} 
             
        
    
    
        This is an example where an add-on could be desired.
    
  
    
        
        There is some boilerplate code to write to get
        a custom request context type but, when it's done, it's pretty easy to tweak
        and extend.
    
   
      
        In fact, we highly recommend that you use a custom request context as soon as possible
        in your application. That way, you can easily add add-ons when you need them.
    
    
        If you use the Quick Start
        as a start for your application, the customization of
        the request context type has already been done for you!
        But if you start from scratch, an upcoming section will show you how to
        extend the default request context type, by yourself.
    
 
{#==========================================
Section "Request Context / Core request context add-ons"
==========================================#}     
    
        
        The core request context add-ons
    
    
        Similarly to the required components, there are add-ons which are always available,
        in any Spincast application.
        
        Here, we'll show some examples for each of them, but you'll have to 
        go to the associated plugin documentation page to see their full API.
    
    
        
            - 
                
IRequestRequestContextAddon<R> request()
                
                    The request() add-on allows access to
                    information about the current request: its body, its headers, its URL, etc. The default
                    implementation, SpincastRequestRequestContextAddon, is provided by the 
                    Spincast Request plugin. Check this plugin's documentation
                    for all the available API.
                    
                    
Examples :
                    
                        
                            
// Get the request full URL
String fullUrl = context.request().getFullUrl();
// Get the request body as a JsonObject
IJsonObject body = context.request().getJsonBodyAsJsonObject();
// Get an header
String authorization = context.request().getHeaderFirst(HttpHeaders.AUTHORIZATION);
// Get a queryString parameter
String page = context.request().getQueryStringParamFirst("page");
// Get the value of a dynamic path token.
// For example for : /users/${userId}
String userId = context.request().getPathParam("userId");     
                         
                       
                
             
        
    
    
        
            - 
                
IResponseRequestContextAddon<R> response()
                
                    The response() add-on allows you to build
                    the response : its content, its content-type, its HTTP status, its headers.
                    The default implementation, SpincastResponseRequestContextAddon, is provided by the 
                    Spincast Response plugin. Check this plugin's documentation
                    for all the available API.
                    
                    
Examples :
                    
                        
                            
// Set the status code
context.response().setStatusCode(HttpStatus.SC_FORBIDDEN);
// Add an header value
context.response().addHeaderValue(HttpHeaders.CONTENT_LANGUAGE, "en");
// Set the content-type
context.response().setContentType(ContentTypeDefaults.JSON.getMainVariation());
// Permanently redirects to a new url (the new url
// can be absolute or relative)
context.response().redirect("/new-url", true);
// Send some bytes
context.response().sendBytes("Hello World".getBytes("UTF-8"));
// Send an object as Json
context.response().sendJson(user);
// Send HTML evaluated from a template
context.response().sendHtmlTemplate("/templates/user.html", params);     
                         
                    
                
             
        
    
    
        
            - 
                
ICookiesRequestContextAddon<R> cookies()
                
                    
                    The cookies() add-on allows you to read
                    and write cookies.
                    
                    
Examples :
                    
                        
                            
// Get a cookie
ICookie cookie = context.cookies().getCookie("someCookie");
// Add a cookie
context.cookies().addCookie("someCookie", "someValue");
// Delete a cookie
context.cookies().deleteCookie("someCookie");     
                         
                         
                
             
        
    
    
    
        
            - 
                
IRoutingRequestContextAddon<R> routing()
                
                    The routing() add-on allows you to get
                    information about the current routing process.
                    
                    
Examples :
                    
                        
                            
// Get all the matches returned by the router.
IRoutingResult<IDefaultRequestContext> routingResult = context.routing().getRoutingResult();
// Get the current match : the route handler, its position
// and its parsed path parameters.
IRouteHandlerMatch<IDefaultRequestContext> currentMatch =
        context.routing().getCurrentRouteHandlerMatch();
// Is the current route a "Not found" one?
boolean isNotFoundRoute = context.routing().isNotFoundRoute();
// Are we currently on a route to handle an exception?
boolean isExceptionHandling = context.routing().isExceptionRoute();     
                         
                    
                
             
        
    
    
    
        
            - 
                
ITemplatingRequestContextAddon<R> templating()
                
                    The templating() add-on gives access to templating functionalities.
                    
                    
Examples :
                    
                        
                                {% verbatim %}
Map<String, Object> params = new HashMap<>();
params.put("name", "Stromgol");
// Evaluation of an inline content 
String html = context.templating().evaluate("Hello {{name}}", params);
// Evaluation of a template file
String html = context.templating().fromTemplate("/templates/user.html", params);{% endverbatim %}   
                         
                    
                
             
        
    
    
    
        
            - 
                
IVariablesRequestContextAddon<R> variables()
                
                    The variables() add-on allows you to add
                    variables which are request scoped. This is a good way to make a
                    route handler communicate some informations to others.
                    
                    
Examples :
                    
                        
                            
// Get a request scoped variable as a JsonObject.
IJsonObject info = context.variables().getAsJsonObject("someObjectName");
// Get a request scoped variable as a String.
String info = context.variables().getAsString("someKey");
// Add a new request scoped variable 
context.variables().add("someKey", "someValue");
                         
                       
                
             
        
    
    
{#==========================================
Section "Request Context / Core request context methods"
==========================================#}     
    
        
        The core request context methods
    
    
        In addition to the add-ons we just listed, there are also
        some utility methods on the request context which are always
        available:
    
    
        
            - 
                
IJsonManager json()
                
                    Provides easy access to the IJsonManager,
Json related methods.
                
             
            
            - 
                
IXmlManager xml()
                
                    Provides easy access to the IXmlManager,
XML related methods.
                
             
            
            - 
                
Injector guice()
                
                    Provides easy access to the Guice context.
                
             
            
            - 
                
<T> T get(Class<T> clazz)
                
                    Shortcut to get an instance from Guice. Will also cache the instance (as long as it is request scoped or a singleton).
                
             
            
            - 
                
<T> T get(Key<T> key)
                
                    Shortcut to get an instance from Guice. Will also cache the instance (as long as it is request scoped or a singleton)
                
             
            
            - 
                
Locale getLocaleToUse()
                
                    The best Locale to use, as found by the 
                    LocaleResolver.
                
             
            
            - 
                
Object exchange()
                
                    The underlying exchange object, as provided by the HTTP server.
If you know for sure what the 
                    implementation is, you may cast this object to access extra functionalities not provided by Spincast out of the box!
                
             
        
     
 
       
{#==========================================
Section "Request Context / Extending the request context type"
==========================================#}     
    
        
        Extending the request context
    
    
        Extending the request context is probably to most advanced thing to
        learn about Spincast. Once in place, a custom request context is quite 
        easy to adjust and extend, but the required code to start may be somewhat challenging. 
        This is why we recommend that you start your application with the Quick Start:
        this application already contains a custom request context type,
        so you don't have to write the bootstrapping code by yourself! But if you start from scratch or if you
        are curious about how a custom request context type is possible, keep
        reading.
    
    
        
        First, let's quickly repeat why we could want to extend the default request context type...
        We may have a "translate(...)" method which is frequently use by our various
        route handlers. Let's say this is a method helping translate a sentence from one language
        to another. 
    
    
        Instead of injecting the class where this method is
        defined each time we need to use it, wouldn't it be nice if we would have access to it
        directly from the request context? For example:
    
    
        
            
                
public class AppController {
    public void myRouteHandler(IAppRequestContext context) {
        String translated = context.translate("Hello World!", Locale.ENGLISH, Locale.FRENCH);
        // ...
    }
}
             
        
    
    
    
        Since this method doesn't exist on the default IRequestContext interface,
        we'll have to create a custom type and add the method to it. In the previous snippet, 
        this custom type is called "IAppRequestContext".
    
    
        Let's create this custom request context interface:
    
    
        
            
                
public interface IAppRequestContext extends IRequestContext<IAppRequestContext> {
    public void translate(String sentense, Locale from, Locale to);
    // Other custom methods and/or add-ons...
}
            
        
    
    
        Note that we extend IRequestContext, which is the
        base interface for any request context type, but we parameterize it using our custom type.
        This is required because this custom type is used in some of the base methods' signatures.
    
    
        Then, the implementation:
    
    
        
            
                
public class AppRequestContext extends RequestContextBase<IAppRequestContext>
                               implements IAppRequestContext {
    @AssistedInject
    public AppRequestContext(@Assisted Object exchange) {
        super(exchange);
    }
    @Override
    public String translate(String sentense, Locale from, Locale to) {
        
        // More hardcoded than translated here!
        return "Salut, monde!";
    }
}
            
            
                Explanation :
                
                    - 
                        1 : We extend 
RequestContextBase, 
                        the default implementation class of a request context, since we want to
                        keep the default methods and simply add one. We also need to parameterize
                        this base class with our custom request context type.
                     
                    - 
                        2 :We implement our custom interface.
                    
 
                    - 
                        4-7 : The base class requires the server's
                        
exchange object, which is going to be injected using an 
                        assisted factory. Don't
                        worry too much about this. Simply add this constructor, and things should be working.
                     
                    - 
                        9-14 : We implement our new 
translate(...) method.
                     
                
              
        
    
    
        Last, but not the least, we need to tell Spincast about
        our custom request context type! This is done by overriding the
        getRequestContextImplementationClass() method of the core module :
    
    
        
            
                
public class AppModule extends SpincastDefaultGuiceModule {
    @Override
    protected void configure() {
        super.configure();
        // ...
    }
    @Override
    protected Class<? extends IRequestContext<?>> getRequestContextImplementationClass() {
        return AppRequestContext.class;
    }
}
             
        
    
    
        Note that it is the implementation, "AppRequestContext", that we have to specify, 
        not the interface!
        This is to simplify your job: Spincast will automatically find the associated
        interface and will use it to parameterize the required components.
    
    
        And you are done! From now on, when you are using a routing related component which has to be parameterized with the
        request context type, you use your custom type. For example:
    
    
        
            
                
IRouter<IAppRequestContext> router = getRouter();
router.GET("/").save(context -> {
    String translated = context.translate("Hello World!", Locale.ENGLISH, Locale.FRENCH);
    // do something with the translated sentence...
});
             
        
    
    
        Or, using an inline route handler: 
    
    
        
            
                
IRouter<IAppRequestContext> router = getRouter();
router.GET("/").save(new IHandler<IAppRequestContext>() {
    @Override
    public void handle(IAppRequestContext context) {
        String translated = context.translate("Hello World!", Locale.ENGLISH, Locale.FRENCH);
        // do something with the translated sentence...
    }
});
             
        
    
    
    
        All this, again, may seem like a lot of boilerplate code. But it has to be done only one time and, once in place, 
        it's easy to add new methods and add-ons to your request context!
    
    
        
        Using unparameterized components
    
    
        One last trick about the extending the request context. 
        You can do for your custom type what we did for the
        default one: to create an unparameterized version for each component which, at the base, must
        be parameterized with it. For example, here's how the default router interface is defined:
    
    
        
            
                
public interface IDefaultRouter extends IRouter<IDefaultRequestContext> {
    // nothing required
}
             
        
    
    
        This interface has no other goal than to "hide" the parameterization.
        Because of this definition, you can inject IDefaultRouter 
        instead of IRouter<IDefaultRequestContext>, which is arguably simpler. Both
        types are interchangeable.
    
     
        You can do the exact same thing with your custom route context type.
        For example:
    
    
        
            
                
public interface IAppRouter extends IRouter<IAppRequestContext> {
    // nothing required
}
             
        
    
     
        Now, you could inject IAppRouter instead of IRouter<IAppRequestContext> when you
        need your custom router. Here again, it's a matter of taste! Some prefer those simpler 
        interfaces to the generics ones.
    
    
        For more details, have a look at the Quick Start code. It implements exactly
        that.