DEV Community

Cover image for Web Services in Java 2 - Provide REST Resources
Jonas Funcke
Jonas Funcke

Posted on • Edited on

Web Services in Java 2 - Provide REST Resources

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\"}";
            }
    }
Enter fullscreen mode Exit fullscreen mode

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;
        }
    }
Enter fullscreen mode Exit fullscreen mode

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;
            }
    }
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

I hope you had fun and everything worked out for your. Have an awesome day! :D

Top comments (0)