Spring + REST + Angular: Creating the API — Part 1 of 2

computer script

This series of posts builds a RESTful service that is consumed by an Angular front end. In this post we will create a RESTful web service built on Spring MVC that uses JSON. The source code for this sample application is on GitHub:
https://github.com/thoward333/addrbook

Service-Oriented Architecture

With the growing popularity of Single Page Application (SPA) and mobile applications, Service-Oriented Architecture (SOA) is shining now more than ever. Web services, by nature, are language agnostic, so they can be re-used across applications written in any language whether it’s a JavaScript, Objective-C, Java, or .NET application. SOA is fundamentally based on exposing data via web services that empower the client applications to render that data in an appropriate way. This differs from traditional architectures that only expose HTML pages for display in the browser. The SOA approach is ideally suited for multiple clients. For example: A traditional J2EE application would typically query the database and use a JSP template to render the data. This has very little re-use when trying to build a new mobile application to do the same thing (an increasingly common need). What if that traditional application were to instead fetch the same data from a web service? By making our traditional application a consumer of a web service, we would well positioned to create that new mobile application as a new consumer to the existing service.

A REST (REpresentational State Transfer) web service is one where the URL identifies the data and the HTTP verb communicates what operation to take on that data. A GET request reads data, POST creates data, PUT updates data, and DELETE deletes data. The URLs in RESTful services are hierarchical in nature. For example: ‘GET /users’ would return a list of all users and ‘GET /users/1’ would return the user with id 1. Similarly, a ‘POST /users’ would create a user, using the request body to provide the data for the user to be created.

RESTful services can use any data format, although most use XML or JSON (JavaScript Object Notation). When designing web services for JavaScript-based consumers, JSON has several benefits. First, JSON is natively supported by JavaScript which results in clean, expressive code on the client side. Second, JSON is less verbose than XML which results in reduced bandwidth usage, especially important for mobile clients. Third, perhaps subjectively, JSON is more human-readable, which makes debugging easier.

Spring

Spring Core is a Dependency Injection (DI) framework that uses an Inversion of Control (IoC) container to isolate components and promote a loosely coupled system. See this earlier blog post for more details on Spring.

Spring MVC is a framework that binds URLs to Java code. Traditionally it was used as a platform for traditional J2EE applications, using JSPs to render HTML content. The modern spin is to leverage Spring MVC’s powerful URL binding system to define a RESTful web service.

Spring Test is a powerful unit test library that provides a JUnit Runner that simplifies creating application contexts in test cases. It also contains an API for mocking the full MVC stack, allowing unit tests to see the fully rendered response as a client would see it (HTTP status and all). This allows us to create integration tests that do not depend on a J2EE container (eg, embedded Jetty server), although there are still many reasons to create acceptance tests using an actual J2EE container.

Jackson is a set of libraries for marshaling and unmarshaling JSON content. It integrates seamlessly with Spring MVC; simply add the libraries to the classpath and your application is ready for JSON (no additional configuration required).

Look Ma, No XML!

The J2EE Servlet 3.0 spec introduced the ability for a WAR to omit the web.xml. Spring 3.0 introduced annotation-driven configuration, but this still had to be bootstrapped in web.xml. Spring 3.1 introduced the WebApplicationInitializer interface, which allows bootstrapping purely in Java. Our Address Book application will contain no XML (except for Maven pom.xml). Java bootstrapping can do everything XML bootstrapping can do, and vice versa. While it’s a stylistic decision, I find the Java bootstrapping to be more expressive–not to mention compile-time checking of class names, etc.

public class WebAppInitializer implements WebApplicationInitializer {
  public void onStartup(ServletContext servletContext) throws ServletException {
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(RootConfig.class);
    rootContext.refresh();

    servletContext.addListener(new ContextLoaderListener(rootContext));
    servletContext.setInitParameter("defaultHtmlEscape", "true");

    AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
    mvcContext.register(MvcConfig.class);

    ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(mvcContext));
    dispatcher.setLoadOnStartup(1);
    Set mappingConflicts = dispatcher.addMapping("/api/*");
  }
}

Bootstrapping

Stepping through the above WebAppInitializer snippet, two application contexts are defined. The root context and the web context, the latter is also bound to a dispatcher servlet mapped to the ‘/api’ prefix. It’s important that we bind the dispatcher servlet to something other than root, since we will be serving the Angular application from the same WAR (see Part 2). Let’s take a look at RootConfig:

@Configuration
@Import(DatabaseConfig.class)
@ComponentScan(basePackages = { "com.trey.addrbook.service", "com.trey.addrbook.dao", "com.trey.addrbook.util" })
public class RootConfig {

}

The @Configuration annotation indicates that it contains Spring configuration. The @ComponentScan annotation loads all components in the specified packages. The @Import annotation registers another @Configuration class. This allows encapsulation of different types of components, which improves maintainability.

Now let’s take a look at DatabaseConfig:

@Configuration
@EnableTransactionManagement
public class DatabaseConfig {

  @Bean
  public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:init.sql").build();
  }

  @Bean
  public PlatformTransactionManager txManager() {
    return new DataSourceTransactionManager(dataSource());
  }
}

The @EnableTransactionManagement annotation enables Spring’s transaction advice, which we configure in the 2nd bean. The @Bean annotation tells Spring to add the value to the application context. The bean’s id is method name. For those familiar with XML-driven Spring, this is equivalent to the ubiquitous <bean> tag. The Address Book application utilizes an HSQL Embedded database. Spring has great support for embedded databases, as we can see by the one-liner to start an embedded database.

Now let’s take a look at MvcConfig:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.trey.addrbook.controller")
public class MvcConfig extends WebMvcConfigurerAdapter {

}

Similar to RootConfig except that we’re scanning the controller package, which contains our special @Controller class. Per best practices, we are only adding the controller beans to web context, putting all other beans in the root context. The @EnableWebMvc activates the Spring MVC feature set on all @Controller classes.

PersonController

@Controller
public class PersonController {

  private PersonService personService;
  private DtoFactory personDtoFactory;

  @Autowired
  public PersonController(PersonService personService, DtoFactory personDtoFactory) {
    this.personService = personService;
    this.personDtoFactory = personDtoFactory;
  }
...
}

The @Controller annotation does two things. One, it declares PersonController as a component (aka Spring bean) that enables dependency injection (DI). Second, it enables the Spring MVC feature set, including URL binding. The @Autowired annotation tells Spring to provide the PersonService and DtoFactory beans using DI, wiring by type. If the Spring context has multiple beans of the same type, use the @Qualifier annotation to specify which one. (Alternatively, you can use the JSR-250 @Resource annotation to wire by name.) Note: The @Autowired annotation can be used in each field or on the constructor, as it is used here.

Let’s take a look at the getPersonById method:

  @RequestMapping("person/{id}")
  @ResponseBody
  public PersonDto getPersonById(@PathVariable Integer id) {
    return personDtoFactory.createPerson(personService.getPersonById(id));
  }

The @RequestMapping binds this method to the given URL. This annotation also specifies the HTTP verb to bind–omitted here since the default value is GET. Note: The @RequestMapping annotation can also be provided at the class level. When both are used together, the class URL is prepended to the method URL for binding. The {id} in the mapping indicates a path variable, which is bound using the @PathVariable annotation. Variables can also be provided as query string parameters, as we’ll see in the getPersonByIdFromParam:

  @RequestMapping(value = "person", params = "id")
  @ResponseBody
  public PersonDto getPersonByIdFromParam(@RequestParam Integer id) {
    return personDtoFactory.createPerson(personService.getPersonById(id));
  }

Both are functionally equivalent. Per REST best practices, path variables should be used when specifying the target data (eg, unique identifier) and use the query string for filtering/sorting. In this case, a path variable is more appropriate. Distinguishing between the two is not always clear and can be subjective in some circumstances.

Caution: Never bind an optional variable to the path, since a missing path variable would resolve to an entirely different method (remember that a ‘//’ in a URL are treated like a single ‘/’). With a bug like that, you’ll be lucky if you just get a 404 error–you might accidentally invoke a different method!

The @ResponseBody annotation tells Spring to bind the method’s return value to the HTTP response. In this case, the method returns a PersonDto object. Here’s where Jackson comes into play. Jackson will then marshal JSON for the PersonDto object, which is what the client will see.

Note: I strongly recommend avoiding entity/domain objects in the method signatures for an @Controller class. By creating custom DTOs you gain the ability to optimize the data transfer for the method. Also keep in mind that if your domain models are also JPA entities for an ORM system (eg, Hibernate beans), then returning them could result in undesired database queries to resolve lazy relationships or potentially even fail if the ORM session has already been closed. That topic is outside the scope of this discussion, but I’ve been bitten by that in the past.

Error Handling

So what happens if we don’t find a person with id 1? RESTful web services use HTTP error codes for these scenarios. An HTTP 404 error code (page not found) is commonly used when data is not found. Here’s how we do that in Spring MVC:

  @ExceptionHandler(PersonNotFoundException.class)
  @ResponseStatus(HttpStatus.NOT_FOUND)
  @ResponseBody
  public String handlePersonNotFoundException(PersonNotFoundException e) {
    return e.getMessage();
  }

The @ExceptionHandler annotation catches the PersonNotFoundException–which is thrown from the Data Access Object (DAO), PersonDaoImpl, for this scenario)–and provides alternate behavior, much like a try-catch. In this case, the @ResponseStatus annotation specifies the 404 HTTP error code (type-safe enumerated value for 404) and the exception’s message is written to the response (I figured it’d be nice to have that information during debugging!). Note: The @ExceptionHandler doesn’t have to return a String. It has all the same robust response functionality as the other methods.

Testing

I’ll let you explore the rest of the code on your own. Let’s switch gears and talk about Spring’s JUnit support. Here’s the code for ControllerTestConfig:

@Configuration
@ComponentScan(basePackages = { "com.trey.addrbook.util" })
public class ControllerTestConfig {

  @Bean
  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  public PersonService mockPersonService() {
    return Mockito.mock(PersonService.class);
  }

}

This is a custom Spring context that will be used exclusively for unit testing PersonController. It only scans util package (for DtoFactory), since we will be mocking out PersonController’s other dependencies. (Arguably the DtoFactory should be mocked as well, but given the trivial nature of the DtoFactory (copies properties from one class to another) it would be more work to mock it than to just use the real bean.) Next, we define a PersonService bean that is a mock object. If you’re not familiar with Mockito, think of it as a way to provide a fake instance of PersonService that allows individual method calls to be faked on the fly.

The ability to mock dependencies is crucial in unit testing, since using a real PersonService would make TestPersonController an integration test. See TestEndToEnd for an example of an integration test. By definition, a unit test must isolate the component being tested. To be clear, this Spring context does NOT define a PersonDao or embedded database.

Now let’s look at TestPersonController:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ControllerTestConfig.class })
public class TestPersonController {

  @Autowired private PersonService mockPersonService;
  @Autowired private DtoFactory dtoFactory;

  private MockMvc mockMvc;

  @Before
  public void setUp() {
    mockMvc = MockMvcBuilders.standaloneSetup(new PersonController(mockPersonService, dtoFactory)).build();
  }

  @Test
  public void test_getPersonById() throws Exception {
    ControllerTestFixture f = new ControllerTestFixture();
    Person person = f.createTrey();
    when(mockPersonService.getPersonById(anyInt())).thenReturn(person);

    mockMvc.perform(get("/person/{id}", 1)
        .accept(TestUtil.APPLICATION_JSON_UTF8)
        )
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id", is(1)))
        .andExpect(jsonPath("$.fullname", is(person.getFirstName() + " " + person.getLastName())))
        .andReturn();
  }
...
}

There’s a lot going on here, so let’s step through it. First, there’s the @RunWith(SpringJUnit4ClassRunner) annotation, which lets us run the unit test with our custom ControllerTestConfig by using the @ContextConfiguration annotation. The @Autowired annotations are injected as the tests start. The setUp method creates the MockMvc instance for PersonController, which is part of the spring-test library (not Mockito).

Now we’re ready to look at the test_getPersonById method. The ControllerTestFixture is just a helper class that encapsulates our test data, to improve maintainability of the tests. This line uses Mockito to provide behavior for the mock PersonService bean:

    when(mockPersonService.getPersonById(anyInt())).thenReturn(person);

This means that when PersonService.getPersonById is invoked we will return the sample Person object we created using ControllerTestFixture. Next, we call mockMvc.perform, which dispatches an HTTP request against Spring MVC just as if an inbound network request was made. These lines do a ‘GET /person/1’ with an accepts header for application/json:

    mockMvc.perform(get("/person/{id}", 1)
        .accept(TestUtil.APPLICATION_JSON_UTF8)
        )

The remaining lines are expectations validating that the response contains the expected data. The use of the static method jsonPath demonstrates that JSON is a first-class citizen in Spring MVC. This powerful helper method makes validating the JSON content easy to read and understand.

Bringing It All Together

You can build the project using Maven and deploy it to any J2EE container that supports the Servlet 3.0 spec (eg, Tomcat 7.0.15+). Once deployed you can open your browser to http://localhost:8080/addressbook/api/person/1 and see a JSON response that looks like this:

{"id":1,"fullname":"Trey Howard"}

That concludes Part 1 of this series. In Part 2 we will create an Angular application to consume this service.