Hi, welcome back. Hopefully everything we did in part 1 worked out for you. If anything didn't please leave us a comment and we will look into it as soon as possible.
In this post we will learn how a REST handler looks in Java and what we can do to tell our server that is exists.
How are REST Resources structured and how will we access them?
In general, a common approach for structuring Java Applications, is to group actions into logical groups. E.g. all calls working with Users as their target will be grouped into a class named UsersController. In general, the most common HTTP Request Methods are being associated with a concrete action type:
- GET - fetch data
- POST - create data
- PUT/PATCH - update data
- DELETE - destroy data
There are of course lots of other request methods but we will mainly focus on those four as they provide nearly all capabilities we need for our small project.
Here we can look at a small example controller:
@Path("")
public class ExampleResource {
@GET
@Produces(MediaType.TEXT_HTML)
public String hello() {
return "<h1>Hello World</h1>";
}
@GET
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public String test() {
return "{ \"Hello\": \"World\"}";
}
}
The @Path
Annotation tells the server the base path the controller is operating at. This annotation can also be put on top of the handler Method. The path this method is listening at will then be created by joining the strings from the annotation on the class and on the method using a/
Every Method, that should handle a Request, will also get an annotation with the name of the HTTP Method it supports. In this small example our two methods only support GET.
Tell the server what we want and what we give
With @Produces
and @Consumes
we tell the server what type of content our method wants and what type of content it will return back to the consumer. In the Javax.ws.rs specification, there is a Enum provided that's called MediaType. It contains all the media types supported by Java. To serve HTML, we will pass MediaType.TEXT_HTML
to the @Produces
annotation. If we want to get some JSON from the request, we just tell the server in the @Consumes
annotation with MediaType.APPLICATION_JSON
.
Pass input with the request
That's nice and all, but how the heck can we check if the input from our request is cool?
No biggie. We just gonna create a class for it:
public class MyFancyInput
private String username;
private Integer age;
private Boolean acceptTOS;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getAcceptTOS() {
return this.acceptTOS;
}
public void setAcceptTOS(Boolean acceptTOS) {
this.acceptTOS = acceptTOS;
}
}
A request body object is just a plain Java POJO. All the properties must have a getter and a setter method as the deserialization will try to call them. To ensure that a null field will not break our service, we only use non-primitive datatypes. This is because boolean
for example does not support null types but Boolean
does.
When you input class is done, we will just tell our operation that we want that as parameter:
@Path("")
public class ExampleResource {
@GET
@Produces(MediaType.TEXT_HTML)
public String hello() {
return "<h1>Hello World</h1>";
}
@GET
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public String test() {
return "{ \"Hello\": \"World\"}";
}
@POST
@Path("test")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public MyFancyInput getInput(@NotNull MyFancyInput input) {
return input;
}
}
Here we just get the input and return it to the client again. With the @NotNull
annotation we tell the server that the parameter for this method is required and cannot be null.
If the input comes from a special place in the request, we just annotate it with the right annotation. Available annotations are:
@BeanParam
@CookieParam
@FormParam
@PathParam
@QueryParam
@MatrixParam
Link it all together
We have the resource, we have the data structure, and if you did Part 1, you also have the server. Now we need to connect it all:
In our main class, we just add something to our ResourceConfig:
// Holds all the resources we want to register (currently, there's nothing to do)
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(ExampleResource.class); // our rest resource
// the parser for JSON and XML request bodies
resourceConfig.register(JacksonFeature.class);
Now when we run our server, we should be able to reach the resources we just create at localhost:8080/
and on localhost:8080/test
After all of this, our project structure should look something like this:
src
- main
- java
- Main.java
- ExampleResource.java
- MyFancyInput.java
- test
I hope you had fun and everything worked out for your. Have an awesome day! :D
Top comments (0)