Rest controllers¶
The examples so far always relied on modifications of the index page of the Song controller. Although it is easy for displaying tutorial examples this approach is highly inappropriate for real-world scenarios. Luckily, Glagol DSL provides a built-in Rest API support in controllers.
Index action¶
Every rest controller supports the standard Rest actions which are based on the well-known HTTP 1.1 request methods. As you might have noticed already, so far we used the index
action for testing. Generally speaking, when we want to run the index we simply make a GET
request to the route that the controller defines. The response can be any value. Usually, the Rest index will return a list of resources. In our case, this fits with the result of querying for all entities. Therefore, lets modify our Songs index to select all song entities:
namespace MusicLib
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
}
We already had this exact bit in the previous tutorial. Lets keep it this way!
Show action¶
What if we want to request a single resource? For this task Glagol DSL provides the developers with a show
action. Lets introduce it:
namespace MusicLib
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
show (int id) {
return songs.find(id);
}
}
The main difference is that the show action accepts an argument which is used as a criteria for finding a resource. In the example above we rely on the existing find
repository method that will do the job.
Now compile the sources and call the following curl command:
$ curl localhost:8081/song/1
Naturally, the show
action is initiated by a GET
request method in combination with an argument appended to the end of the route. Therefore, a show
request URI has the /song/{parameter}
format.
However, there is an alternative way of finding entities when we rely on the primary key field that is annotated with @id
. For the purpose, we can even avoid our manually created find
method or the repository altogether. To put it simply, developers can utilize the @autofind
annotation to the first parameter of the action like this:
namespace MusicLib
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
show (@autofind Song song) {
return song;
}
}
Lets look at the differences. Instead of accepting int id
as a parameter, we type Song song
preceded by the @autofind
annotation. This very annotation tells Glagol DSL to attempt to find the entity by primary key field and the value of the parameter being passed with the request.
Important
The annotation approach does not require you to have a find
repository method whatsoever. It is a built-in behavior provided the Glagol DSL runtime environment.
Store action¶
The store
method is there to handle POST
requests aimed to create new resources. Additionally, the Glagol::Http::Request
object is necessary for extracting input POST
data from the request:
namespace MusicLib
import Glagol::Http::Request;
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
show (@autofind Song song) {
return song;
}
store (Request request) {
Song song = new Song(request.input("title"), request.input("author", "unknown"));
songs.save(song);
return song;
}
}
Do not forget to compile the source!
Hint
The Glagol::Http::Request
object is provided by the Glagol DSL standard library. It provides the following methods for extracting POST
data:
string input(string key);
string input(string key, string default);
Use the string default
parameter to provide a default value if the string key
does not exist in the request payload.
Since Glagol DSL relies on Lumen Framework for its runtime both json and x-www-form-urlencoded payloads are supported. For this example we are going to use a json payload:
$ curl -X POST -d '{"title":"Born to raise hell", "author": "Motorhead"}' -H "Content-Type: application/json" localhost:8081/song
Update action¶
The update
action is similar to both show and store actions in a way. First, just like show
it requires a parameter by which to identify a resource. Secondly, just like store
it can use the input()
methods from Glagol::Http::Request
.
In contrast to both actions, the HTTP request method for update
is PUT
. Lets look at this example:
namespace MusicLib
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
show (@autofind Song song) {
return song;
}
store (Request request) {
Song song = new Song(request.input("title"), request.input("author", "unknown"));
songs.save(song);
return song;
}
update (Request request, @autofind Song song) {
song.setTitle(request.input("title"));
song.setAuthor(request.input("author", "unknown"));
songs.save(song);
return song;
}
}
Additionally, the example above requires two setter methods in the entity:
namespace MusicLib
@table="songs"
entity Song {
// ... properties and constructors from before...
public void setTitle(string title) {
this.title = title;
}
public void setAuthor(string author) {
this.author = author;
}
}
Compile the sources and test with curl:
$ curl -X PUT -d '{"title":"Killed by death", "author": "Motorhead"}' -H "Content-Type: application/json" localhost:8081/song/2
Delete action¶
Glagol DSL provides the delete
action to handle DELETE
HTTP requests:
namespace MusicLib
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
show (@autofind Song song) {
return song;
}
store (Request request) {
Song song = new Song(request.input("title"), request.input("author", "unknown"));
songs.save(song);
return song;
}
update (Request request, @autofind Song song) {
song.setTitle(request.input("title"));
song.setAuthor(request.input("author", "unknown"));
songs.save(song);
return song;
}
delete (@autofind Song song) {
songs.remove(song);
}
}
Compile the sources and test with curl:
$ curl -X DELETE localhost:8081/song/1
Create and edit actions¶
Additionally, you can also implement the create
and edit
actions that are basically responding to GET
requests but with additional route extensions.
First, the create
action is usually used to return an initial state entity. Typically this functionality is used to retrieve a blank resource from the service:
namespace MusicLib
rest controller /song {
repository<Song> songs = get selfie;
index {
return songs.findAll();
}
show (@autofind Song song) {
return song;
}
store (Request request) {
Song song = new Song(request.input("title"), request.input("author", "unknown"));
songs.save(song);
return song;
}
update (Request request, @autofind Song song) {
song.setTitle(request.input("title"));
song.setAuthor(request.input("author", "unknown"));
songs.save(song);
return song;
}
delete (@autofind Song song) {
songs.remove(song);
}
create {
return new Song("Please, enter song title", "Please, enter song author (band)");
}
edit (@autofind Song song) {
return song;
}
}
To get the response from this endpoint you need to append /create
to the route like this:
$ curl localhost:8081/song/create
Similarly, edit
is just like show
but it requires the /edit
extension to be accessed:
$ curl localhost:8081/song/1/edit