Controllers - AlexGPlay/Blynder GitHub Wiki
A controller is a bean that is used to map the different URLs that can be navigated.
You can create your own controller by using the @Controller annotation.
@Controller
public class FooController{
...
}This will allow the application to be aware of the controller. Now you have to set up the paths that can be navigated, that is possible using the @Path annotation.
The @Path annotation is used in a method, the method that has the annotation will be the one that handles petitions to that path.
@Controller
public class FooController{
@Path("/path")
public void fooPath(){
...
}
}The next question is, how do I return the view? Well, there is an object called Response that holds that responsability. And how do you use that? There are two ways, you can create your own Response or you can request it in the methods params. Lets see how does it work.
@Controller
public class FooController{
@Path("/path/noParams")
public void fooPath1(){
Response response = new Response();
...
}
@Path("/path/params")
public void fooPath2(Response response){
...
}
}At this point you have your Response object and you need to return the view in some way. There are different views that can be rendered, you can return an HTML file, Swing Component, JavaFX Scene or FXML file. And how do you return it? You have to tell it to the Response object.
@Controller
public class FooController{
@Path("/path/params")
public void fooPath1(Response response){
response.response("file.html");
}
}Using that example, the file.html from the resources of the project will be rendered when that path is called. That way works, but we have something to explain here. You can return that response in two ways. As in the example above asking for a response and setting the view in that response or returning it. The controllers are allowed to work with void return or with Response return.
@Controller
public class FooController{
@Path("/path/noParams")
public Response fooPath1(){
Response response = new Response();
return response.response(new JPanel());
}
@Path("/path/params")
public void fooPath2(Response response){
response.response("file.html");
}
}Both of them work good, you can use whatever you prefer, all the Response methods return the Response instance so you can chain the methods or return the return of any method.
Now we need data to work with. There are different things that we can do, we can create URLs with segments and we can get the query params from the URL. How do we do this?
Well, there are two new things here, the Request object holds all the data we need, the method that was used, the URL, the headers, the params and body. We can ask for that in the method params as we did with the Response.
@Controller
public class FooController{
@Path("/path/noParams")
public Response fooPath(Request request){
Response response = new Response();
return response.response(new JPanel());
}
}That is one way of getting the request, but we can do another thing. Imagine that there is a request to /path/noParams?param=test, there are two ways of getting the value of test. The first one is using the Request object.
@Controller
public class FooController{
@Path("/path/noParams")
public Response fooPath(Request request){
Response response = new Response();
String param = request.getParams().get("param");
return response.response(new JPanel());
}
}That is one way of doing it, but there is another way that may be useful if you don't want all the Request object, for this other method we are introducing the @PathVariable annotaion. It is used this way:
@Controller
public class FooController{
@Path("/path/noParams")
public Response fooPath(@PathVariable String param){
Response response = new Response();
return response.response(new JPanel());
}
}In that case we have now the param value in that variable. But there is a problem here, java compiles the code differently depending on some options, in order to get rid of this problem there is a param that can be given to the @PathVariable annotation, I recommend using it.
@Controller
public class FooController{
@Path("/path/noParams")
public Response fooPath(@PathVariable(name="param") String param){
Response response = new Response();
return response.response(new JPanel());
}
}Now it is perfect, it will extract the param variable from the query params, and now, how do I use segments? Well, it is easy, you have to introduce some curly brackets surrounding the variable you want to have there, lets get an example for a better explanation.
@Controller
public class FooController{
@Path("/path/noParams/{name}")
public Response fooPath(@PathVariable(name="param") String param, @PathVariable(name="name") String name){
Response response = new Response();
return response.response(new JPanel());
}
}As you can see there, we now have a path that answers to /path/noParams/{name}, that name can be anything and that will be stored as PathVariable, that data can be retrieved from the Request object or using the @PathVariable syntax.
There is another type of Controllers in the project, those are ApiControllers, this controllers doesn't render anything, the Response they fill will return a String. Well... what is the utility of this? There are times where you just want to do some logic in the back and not render the view, for example, in an AJAX call. In that case you want to send some data to the back, do some operations and return a response to the same view. So now we have this APIControllers that can do that, but there is one more thing, the @Path annotation holds another value, the path method. When you dont specify it it gets GET as default but you can set it. Lets see an example.
@ApiController
public class FooController{
@Path("/path/noParams/{name}", method="GET")
public Response fooPath(@PathVariable(name="param") String param, @PathVariable(name="name") String name){
Response response = new Response();
...
return response.response("OK");
}
}That is an APIController working and as you have seen there is a method in the @Path, as you can imagine it can be GET, POST, DELETE or whatever you want it to be.
There is probably a question here, and that is, how do I make an AJAX request if there is no server? Well, there is a special way to do it, you have to use the app:// schema instead of http://, so for example, using the fetch syntax we can do the following request:
fetch("app://path/noParams/foo").then(...)
There are a few things that must be known if you are going to work with all of this. The @Path method will be always overriden to GET if you are using a normal Controller, the method will be only listened to if you are using ApiControllers. And there are other things, those things are related to the Response object. The response object hold more properties than the returning response.
- StatusCode, you can set the status code of the response, the default one is 200 but there are times when it will be changed to 404 or 500.
- Redirect, this is an interesting one, you can tell a controller to do some actions and after that redirect to another path, this can be used if you want to do some logic in a base controller and after that go to another view.
- CanContinue, this one will be ignored for now, we will return to it in the Filters.
- ResponseType, you can ignore this one, it is automaticaly completed.
There is also a built-in template engine for the HTML views, it is Thymeleaf, we can interact with it with the th tags and we can send variables into it using the Model class, this class can be requested in the parameters of the controller and it only stores a hash that will be later used. For example:
public void fooMethod(Response response, Model model){
model.setVariable("foo","some data");
response.response("foo.html");
}That could be a controller method that does some things and then wants that the foo variable is rendered in the view, lets see how the view could be.
<html>
<head>...</head>
<body>
...
<p>The Foo variable holds the value:</p>
<p th:text=${foo}></p>
...
</body>
</html>For more information about how to use Thymeleaf you should go their documentation page.
As in the Model example, you can get a WindowSize object that allows you to change the size for a view, you just have to ask for it and set the size you want.
public void fooMethod(Response response, WindowSize size){
size.setDimensions(500, 250);
response.response("foo.html");
}That way you can set a size that fits your view the most.