templates.docs.quickTutorial.html Maven / Gradle / Ivy
Show all versions of spincast-website Show documentation
{#==========================================
Docs : "Quick Tutorial"
==========================================#}
Quick Tutorial
Here, we'll present a quick tutorial on how to develop
a Spincast application : as a traditional website or as a
SPA
application. We won't go into too much details
so, to dig deeper, have a look at :
-
The dedicated Demos/Tutorials section of the site.
-
The Quick Start application, which is a fully
working Spincast application.
-
The others sections of this documentation!
{#==========================================
Section "Quick Tutorial / Traditional website"
==========================================#}
1. Traditional website
A "traditional website" is a web application where the server generates
HTML
pages, using a Templating Engine.
This is different from the more recent
SPA approach, where the interface is generated
client side (using javascript) and where the backend only provides REST services
(by returning Json
or, more rarely these days, XML
).
1.1. Bootstrapping
Bootstrapping a Spincast application involves 3 main steps :
-
Using the Bootstrapper to initialize your
application. This is where you specify the components to bind and the plugins to install in
order to create the Guice context for your application.
-
Defining
Routes
and Route Handlers
. We're going to
see those in a minute.
-
Starting the HTTP Server.
Here's a quick example of using the bootstrapper :
Spincast.configure()
.module(new AppModule())
.init(args);
Please read the whole section dedicated to
bootstrapping for more information about this topic.
The quickest way to start a Spincast application is to
download the Quick Start application and to adapt it
to your needs.
1.2. Defining Routes
You define some Routes
and you specify which Route Handlers
should handle them. The
Route Handlers
are often methods in a controller but can also be defined inline, directly
in the Route
definitions.
The Routes
definitions can be all grouped together in a dedicated class or can be defined in controllers
(have a look at The Router is dynamic for an example).
You can learn more about the various routing options in the Routing
section, but here's a quick example of Route
definitions :
// For a GET request. Uses a method reference
// to target a controller method as the Route Handler :
router.GET("/books/${bookId}").handle(bookController::booksGet);
// For any HTTP request. Uses an inline Route Handler :
router.ALL("/hello").handle(context -> context.response().sendPlainText("Hello!"));
1.3. Route Handlers
Most of the time, a Route Handler
is implemented as a method in a controller.
It receives a Request Context object as a parameter.
This Request Context
is
extensible and is one
of the most interesting parts of Spincast! In this quick example, we simply use the
default Request Context
implementation,
"DefaultRequestContext"
:
public class BookController {
// Route Handler dedicated to handle GET requests
// for a book : "/books/${bookId}"
public void booksGet(DefaultRequestContext context) {
// ...
}
}
1.4. Getting information about the request
In your Route Handlers
, you use the Request Context
object and its various
add-ons to get the information you need about the current request :
public void booksGet(DefaultRequestContext context) {
// Path parameter
// From "/books/${bookId}" for example
String bookId = context.request().getPathParam("bookId");
// QueryString parameter
String page = context.request().getQueryStringParamFirst("page");
// Field received from a POSTed form
String newTitle = context.request().getFormData().getString("newTitle");
// HTTP Header
String authorizationHeader = context.request().getHeaderFirst("Authorization");
// Cookie
String localeCookieValue = context.request().getCookie("locale");
//...
}
1.5. Building the response's model
You process the current request using any business logic you need, and you build the
model
for the response. This response model
is a JsonObject accessible via
"context.response().getModel()"
: it is the object
where you store all the information you want to return as the response.
You may add to this response model
the variables you want
your templates to have access to :
public void booksGet(DefaultRequestContext context) {
//...
JsonObject book = context.json().create();
book.set("author", "Douglas Adams");
book.set("title", "The Hitchhiker's Guide to the Galaxy");
// Adds the book to the response model
context.response().getModel().set("book", book);
//...
}
1.6. Rendering the response model using a template
When you develop a traditional website, you usually want to render a template
so HTML
is going to be displayed.
To do so, you use the integrated Templating Engine :
public void booksGet(DefaultRequestContext context) {
//... builds the response model
// Sends the response model as HTML, using a template
context.response().sendTemplateHtml("/templates/book.html");
}
1.7. Writing the template
Here is a template example using the syntax of the default Templating Engine
,
Pebble. Notice that the variables
we added to the response model
are available.
{% verbatim %}
{% if book is not empty %}
<div class="book">
<h2>{{book.title}}</h2>
<p>Author : {{book.author}}</p>
</div>
{% else %}
<div>
Book not found!
</div>
{% endif %}
{% endverbatim %}
{#==========================================
Section "Quick Tutorial / SPA website"
==========================================#}
2. SPA / REST services
The main difference between a SPA
application
(or a set of plain REST services
) and a
traditional website, is that in a SPA
you
don't generate HTML
server side.
Instead, most of the logic is client-side, and your Spincast application only acts as a provider
of REST services
to which your client-side application talks using Json
or, more rarely these days, XML
.
2.1. Bootstrapping
Bootstrapping a Spincast application involves 3 main steps :
-
Using the Bootstrapper to initialize your
application. This is where you specify the components to bind and the plugins to install in
order to create the Guice context for your application.
-
Defining
Routes
and Route Handlers
. We're going to
see those in a minute.
-
Starting the HTTP Server.
Here's a quick example of using the bootstrapper :
Spincast.configure()
.module(new AppModule())
.init(args);
Please read the whole section dedicated to
bootstrapping for more information about this topic.
The quickest way to start a Spincast application is to
download the Quick Start application and to adapt it
to your needs.
2.2. Defining Routes
You define some Routes
and you specify which Route Handlers
should handle them. The
Route Handlers
are often methods in a controller but can also be defined inline, directly
on the Route
definitions.
The Routes
definitions can be all grouped together in a dedicated class or can be defined in controllers
(have a look at The Router is dynamic for an example).
In general, if you are building a SPA
, you want to return a single HTML
page : that index page is going to load .js
files and, using those, will bootstrap your
client-side application. Using Spincast, you can return that index page as a
Static Resource, or you
can generate it using a template. Let's first see how you could return the
index page as a Static Resource
:
// The static "index.html" page that is going to bootstrap
// our SPA
router.file("/").classpath("/index.html").handle();
// The resources (.js, .css, images, etc.) will
// be located under the "/public" path :
router.dir("/public").classpath("/myResources").handle();
// ... the REST endpoints routes
As you can see, Spincast will return the
"index.html"
file when a "/"
request is made.
In this HTML
page, you are going to load all the
required resources (mostly .js
files first), and
bootstrap your whole application.
You can also use a template to generate the
first index page. This allows you to dynamically tweak it, to
use variables. Here's an example :
// Inline Route Handler that evaluates
// a template to generate the HTML index page.
router.GET("/").handle(context -> {
// Adds some variables to the response model so
// the template has access to them.
context.response().getModel().set("randomQuote", getRandomQuote());
// Renders the template
context.response().sendTemplateHtml("/index.html");
});
// The resources (.js, .css, images, etc.) will
// be located under the "/public" path :
router.dir("/public").classpath("/public").handle();
// ... the REST endpoints routes
By using such template to send your index page, you have
access to all the functionalities
provided by the Templating Engine.
Note that if your template is quite complexe, you're
probably better creating a
controller to define the Route Handler
, instead of
defining it inline like in our example!
Once the Route for the index page and those for the resources are
in place, you add the ones required for your REST endpoints
.
For example :
// Endpoint to get a book
router.GET("/books/${bookId}").handle(bookController::booksGet);
// Endpoint to modify a book
router.POST("/books/${bookId}").handle(bookController::booksModify);
// ...
2.3. Route Handlers
Most of the time, a Route Handler
is implemented as a method in a controller.
It receives a Request Context object as a parameter.
This Request Context
is
extensible and is one
of the most interesting parts of Spincast! In this quick example, we simply use the
default Request Context
implementation,
"DefaultRequestContext"
:
public class BookController {
// Route Handler dedicated to handle GET requests
// for a book : "/books/${bookId}"
public void booksGet(DefaultRequestContext context) {
// ...
}
}
2.4. Getting information about the request
In your Route Handlers
, you use the Request Context
object and its various
add-ons to get the information you need about the
current request (an AJAX request
for example) :
public void booksGet(DefaultRequestContext context) {
// The Json body of the request as a JsonObject
JsonObject jsonObj = context.request().getJsonBody();
// Path parameter
// From "/books/${bookId}" for example
String bookId = context.request().getPathParam("bookId");
// HTTP Header
String authorizationHeader = context.request().getHeaderFirst("Authorization");
// Cookie
String localeCookieValue = context.request().getCookie("locale");
//...
}
Very often in a SPA
application, or when you develop plain
REST services
, you are going to receive a Json
object
as the body of a request (with a "application/json"
content-type).
In the previous code snippet, context.request().getJsonBody()
gets
that Json
from the request and creates a JsonObject
from it so it is easy to manipulate.
2.5. Creating and sending a Json / XML response
When you receive a request, you process it using any required business logic, and you then build the
Json
(or XML
) object to return as a response. There are two ways to achieve that.
The prefered approach, is to create a typed object,
a book created from a Book
class for example, and explicitly
send this entity as Json
. For example :
public void booksGet(DefaultRequestContext context) {
String bookId = context.request().getPathParam("bookId");
Book someBook = getBook(bookId);
context.response().sendJson(someBook);
}
The second option, probably more useful for
traditional websites though, is to
use the response model
to dynamically create the Json
object to send.
You get the response model as a JsonObject
by calling the context.response().getModel()
method, you
add elements to it and you send it as Json
:
public void booksGet(DefaultRequestContext context) {
// Gets the response model
JsonObject responseModel = context.response().getModel();
// Gets a book
String bookId = context.request().getPathParam("bookId");
Book someBook = getBook(bookId);
// Adds the book to the response model, using
// the "data.book" key
responseModel.set("data.book", book);
// Adds a "code" element to the response model
responseModel.set("code", AppCode.APP_CODE_ACCEPTED);
// Adds a timestamp to the response model
responseModel.set("timestamp", new Date());
// This is going to send the response model as Json
context.response().sendJson();
}
In this example, the generated Json
response
would have a "application/json"
content-type and
would look like this :
{
"code" : 12345,
"timestamp" : "2016-11-06T22:58+0000",
"data" : {
"book" : {
"author" : "Douglas Adams",
"title" : "The Hitchhiker's Guide to the Galaxy"
}
}
}
2.6. Consuming a Json / XML response
You consume the Json
response from your client-side SPA
application whatever it is built with : Angular,
React, Vue.js,
Ember, etc. Of course, we won't go into details here since
there are so many client-side frameworks!
A Json
response can also be consumed by a client
which is not a SPA
: it can be a response for a Ajax request
made using Jquery
or plain javascript. Such Json
response can also be consumed by a
backend application able to send HTTP
requests.