What is HATEOAS?

HATEOAS stands for Hypermedia As The Engine Of Application State. It is a technique that is often used in conjunction with REST APIs.

The basic idea of HATEOAS is that when your API returns a resource, the representation should include links that allow the consumer of the API to discover resources related to the current one. It’s similar to the way websites work. When people open a web page, it contains links to further pages, and they can use these links to navigate without any other information.

Let’s see a simple example where we are getting back the details of a user:

{
    "id": 19,
    "name": "JohnDoe",
    "email": "john.doe@gmail.com",
    "links": [{
            "href": "/users/19/comments",
            "rel": "comments",
            "method": "GET"
        },
        {
            "href": "/users/19",
            "rel": "delete",
            "method": "DELETE"
        }
    ]
}

As you can see we have a links array, which contains two links. One for getting the comments made by the user, and one for deleting the user. Because these links are provided, the consumer of the API do not need to know them. It only has to know where to find them. And of course, it needs to know the possible actions about a resource. In this case, it needs to know in advance that a user has comments and that it can be deleted, otherwise, the rel attribute wouldn’t make too much sense to the client.

This is not the only syntax that can be used for providing the links, but it’s a fairly standard one.

Here is a more real life example from the PayPal API:

{  
  "links": [{
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832",
    "rel": "self",
    "method": "GET"
  }, {
    "href": "https://api.paypal.com/v1/payments/sale/36C38912MN9658832/refund",
    "rel": "refund",
    "method": "POST"
  }, {
    "href": "https://api.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI",
    "rel": "parent_payment",
    "method": "GET"
  }]
}

Why use HATEOAS?

The biggest advantage of this approach is that consumers of the API do not have to hardcode the API URLs into their code. They just have to know where to find them. This allows the API developers to freely change the URL structure as it won’t break the clients.

Limitations

In an optimal case, HATEOAS would allow a consumer to start from a “root” resource and just by going through the links, discover the URL for any endpoint and use it. To an extent, it can work. For example, if we are trying to get the likes received by a user for a specific post, a client could automatically

  • GET the user
  • GET the posts of the user
  • GET the specific post of the user
  • and finally, GET the likes for that specific post

All this based on the links returned in the response for each request.

This approach would not work if we need some extra information to be able to submit the request. For instance, if I want to create a post for a user, I need to know what fields do I have to submit. I can get the target URL from the response, but getting all the information about the fields would be pretty tricky. Even if I would get back the list of fields in the response, I would need to have a logic in place that can determine the correct value for each field.

Summary

The HATEOAS technique can be very useful if we are not trying to overuse it. It’s great when we would like to automatically discover content/endpoints to decouple the clients from the API. But we should not try to construct an API that is fully describing itself using this technique because in most cases, this would not be possible in a dynamic way.

Creating a Spring MVC Maven web application from scratch

This tutorial describes how to create a simple Spring MVC web application statring from a basic Maven project. We set up the required directories and files by hand, almost nothing will be pre-generated.

Tools I used for this tutorial:

Creating the Maven project

  1. In STS press Ctrl + N, this brings up the New wizard where you select what kind of project or file you would like to create. In the search box enter maven. From the filtered list select Maven Project.
  2. Click Next.
  3. Check the Create a simple project (skip archetype selection) checkbox.
  4. Click Next.
  5. Set the following properties:
    • Group Id: com.jtuts
    • Artifact Id: spring-mvc-maven-webapp-from-scratch
    • Packaging: war
    • Name: spring-mvc-maven-webapp-from-scratch
  6. Click Finish.

Of course the Group Id, Artifact Id and Name properties can be set to anything else, these are just example values. However the war value for the Packaging is mandatory, because we are creating a web application and the server which will run it (in our case Tomcat) expects a .war file.

The directory structure

STS will automatically create the basic directory structure for our application. This conforms to the basic structure that Maven expects. You can find more information about it on Maven’s Introduction to the Standard Directory Layout page.

The most important directories for us are:

  • src/main/java: Java source files.
  • src/main/resources: Other resources like property files and xml files.
  • src/main/webapp: Web application source files (e.g. JPSs, images, CSS files).
  • src/test/java: Test source files.
  • src/test/resources: Resources for tests.

By default, Maven expects you to have the above mentioned files in these directories. You can change this in Maven’s pom.xml but you should avoid it not to create confusion when other developers look at your project.

The pom.xml

The pom.xml file is added automatically by STS when you create the Maven project. However, we made some additions that are needed for our webapp to work. This is what the final pom.xml looks like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jtuts</groupId>
    <artifactId>spring-mvc-maven-webapp-from-scratch</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>spring-mvc-maven-webapp-from-scratch</name>

    <properties>
        <spring.version>4.1.0.RELEASE</spring.version>
        <servlet.api.version>3.0.1</servlet.api.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>spring-mvc-maven-webapp-from-scratch</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/${project.build.finalName}</path>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

In this file we include all the dependencies that are required by our example project. We also have a tag which configures the build process of Maven.

The finalName will be the name of the .war file that is created.

Then, we have two plugins:

  1. The tomcat7-maven-plugin can be used to enable Maven to start an embedded Tomcat server and run our app inside it. It also specifies that the context path under which our app will be available (in this case we use the value of the finalName): /spring-mvc-maven-webapp-from-scratch.
  2. With the maven-compiler-plugin we configure the compilation process of Maven. In this case we set that our source code must be compatible with at least Java 1.7, and we want our compiled program to be executable with Java 1.7 and above.

Creating the web.xml file

The web.xml file must be placed in the /src/main/webapp/WEB-INF/ folder is the Web Application Deployment Descriptor of your application. So you have to create the WEB-INF directory in the /src/main/webapp/ directory and inside it create a file named web.xml. This file is an XML document that defines everything about your application that a server needs to know: servlets and other components like filters or listeners, initialization parameters, container-managed security constraints, resources, welcome pages, etc.

The full path for this file is: /src/main/webapp/WEB-INF/web.xml

In this project we will use the following web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <display-name>Spring MVC application from scratch</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:spring/application-context.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

The <web-app> element

This is the root element of you web.xml file.

The attributes inside the <web-app> tag mean that the file uses the 3.0 version of the Servlet Specification.

Defining the DispatcherServlet

The job of the DispatcherServlet is to take an incoming URI and find the right combination of handlers (generally methods on Controller classes) and views (generally JSPs) that combine to form the page or resource that’s supposed to be found at that location.

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Let’s see the definition of the servlet step by step.

<servlet-name>mvc-dispatcher</servlet-name>

Defines the name by which we can reference the servlet.

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

The class that contains the servlet’s code.

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/dispatcher-servlet.xml</param-value>
</init-param>

The contextConfigLocation parameter tells the DispatcherServlet which xml to use for setting up the WebApplicationContext. This will be a child to the root WebApplicationContext defined by ContextLoaderListener and can access every bean in it.

<load-on-startup>1</load-on-startup>

For load-on-startup a value of >=0 means that the servlet is loaded when the web-app is deployed or when the server starts. A value < 0 means that the servlet is loaded whenever the container feels like. Also, the container must guarantee that servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-startup value.

The ContextLoaderListener

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:spring/application-context.xml
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

The ContextLoaderListener creates the root WebApplicationContext for your application. This application context contains all the beans defined in the xml specified as the value of the contextConfigLocation parameter. Also, the beans from this context are available in any child contexts (such as the one we created with the DispatcherServlet).

The dispatcher-servlet.xml file

Create a file named dispatcher-servlet.xml in the src/main/resources/spring directory. It could be anywhere else, but it is good practice to group spring context xml files in a common directory.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

    <context:component-scan base-package="com.jtuts.spring.mvc.webapp.controller" />
    <mvc:annotation-driven />

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/views/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

</beans>

This first line tells spring to go over the classes in the com.jtuts.spring.mvc.webapp.controller package and register them as spring beans if they have the appropriate annotation. In our case we have only one such class which is a controller and this is signified by putting a @Controller annotation on the class.

The <mvc:annotation-driven> tag enables a couple of spring features like mapping requests to controllers, validating form data with the @Valid annotation, using the ConversionService and so on.

The viewResolver bean will handle the resolving of our views (in this case jsp files). The prefix property’s value will be prepended to the name of every view we request and the suffix property’s value will be appended to the requested view name. So, for example if we request the view “home” then the viewResolver will modify this to /WEB-INF/views/home.jsp. This bean enables Spring to find our views, and also lets us write shorter view names because we can omit the common parts from path.

The application-context.xml file

Create file named application-context.xml  in the src/main/resources/spring directory. It should contain the following:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

</beans>

For this basic application this file does not define or configure anything, but when extending our application with additional functionality we can define the beans for our root application context here in the same manner we did in the dispatcher-servlet.xml. So this file currently is just an empty placeholder for future bean definitions and configuration.

The controller

We need a controller to provide a response when the user requests a specific URL. Create a class called HomeController in the com.jtuts.spring.mvc.webapp.controller package, and put the following code in it:

package com.jtuts.spring.mvc.webapp.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HomeController {

    @RequestMapping("home")
    public ModelAndView home2() {
        return new ModelAndView("home");
    }
}

In Spring MVC each controller is a class ann otated with the @Controller annotation. These classes handle incoming requests. A controller can handle multiple requests with multiple methods. The @RequestMapping annotation means that in case of which request urls should spring invoke this method. The method returns a ModelAndView object which contains the view we would like to display and also the model (the data to be displayed in the view). In this case we use the

public ModelAndView(String viewName)

constructor of the ModelAndView class which expects the name of the view to be displayed. Additional model parameters are not retured in this simple example.

The view (jsp) file

We also need a simple jsp file in the project which we will show to the user if they request /home. In this example the full URL, where this page is available is: http://localhost:8080/spring-mvc-maven-webapp-from-scratch/home

Create a views directory in the WEB-INF folder, and inside it create a home.jsp file with the following content:

<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Spring MVC web application from scratch</title>
</head>
<body>
    <h1>Welcome to the home page.</h1>
</body>
</html>

The concrete file will be found by the viewResolver bean we defined earlier. It uses the prefix, the view name we returned from the controller, and the suffix. These concatenated will form the correct path to the home.jsp file.

Starting the web application

In the command line navigate to the root directory of the application and execute the command mvn clean install tomcat7:run. This compiles the project, packages into a .war file, starts an embedded tomcat server and deploys the war file in it.

After the process is completed we can access the app at http://localhost:8080/spring-mvc-maven-webapp-from-scratch/home

Download

You can download the complete source files for this project here.