RSS

Spring Web Basics

09 Mar

Episode 60

Here is the second part of Spring back-end series for beginners. Part zero appeared half year ago, and was focused on Angular front-end. Part one was two weeks ago, and consisted of some basic concepts of Spring framework including inversion of control, dependency injection, beans, configuration and profiles. Today we are going to look into Spring web, in particular web services and handling incoming HTTP requests. There will be no fancy front-end stuff this time, just naked request and response.

steampunk_spider_lamp_by_catherinetterings-d6ugb5v

As usual the introduction to the topic and going over basic concepts might well be at least one separate article but let’s try to do all at once. We will cover the concept of HTTP protocol, servlets, web services, REST and JSON. If you would like to play with complete application working out of the box, visit my GitHub project Spring Angular Intro. This tag corresponds to project state at the time of writing this article.

Foundations

HTTP or Hypertext Transfer Protocol, belongs to the application layer of Internet Protocol Suite, so it’s the highest-level layer above transport layer (like TCP), network layer (like IP) and link layer (like Ethernet). HTTP request contains the HTTP method, address with optional parameters, headers and optional body. There are 9 canonical methods: GET, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT and PATCH, first two being of interest to us in this article. Address with parameters specify a resource we want to retrieve. If more data is required, we may cover that with request body. Finally, headers provide additional information like client browser, language or accepted media type.

Servlet, with a bunch of other interfaces and annotations, is a part of Java EE specification that defines how incoming requests and outgoing responses are handled in compliant Java server. Most importantly, in HTTPServlet, you can map requests to particular Java methods which are given request and response objects as parameters and can manipulate their headers, bodies, status codes etc. as they please. Core part of Spring web module is in fact one fat dispatcher servlet that receives all the traffic and then hunts down the poor victim classes, marked with Spring-specific annotations, that will do the actual job. In assistance of plethora of helpful mechanisms Spring provides.

Web Service is a more general concept, namely a software system that allow machine to machine communication over the network. We often think of Internet as a bunch of machines serving content to humans, which might happen in the end, but much more is often going on in the background. Server that renders your fancy web page probably needs to talk to bunch of other servers to get to database stuff, cache grid, authentication provider, current weather, neighborhood map, spy on your Facebook account, target you with ads or whatever else. There are many technologies there, from JAX-RPC or JAX-WS that use elaborate Web Service Description Language transferred over SOAP protocol to simple and more modern REST architectures.

RESTful or Representational state transfer web services follow the concept of manipulating text-based representations of web resources using well defined methods set, introduced in HTTP 1.1 (HTTP 1.0 only had three: GET, POST and HEAD). Formal constraints indicate that RESTful service should have client-server architecture, should be stateless, client should be able to cache responses and be unable to tell if it’s talking to the server directly or via intermediate components. The resource and it’s representation are two different things, data can be send as XML, HTML or JSON but stored on clay plates if desired.

JSON or Java Script Object Notation is a commonly used data exchange format in RESTful web services. It’s language independent, although it derives from Java Script, human readable and much like JS objects, it stores objects as key – value pairs. Values can be numbers, strings, booleans, arrays, nulls or another object. It corresponds to application/json value of Content-Type header.

steampunk_cthulhu_sculpture_by_catherinetterings-dazvcxq

Talking to the machine

If we are talking machine to machine communication, we need some tool to act like one. Simple GET requests can be issued from browser, just as we do when browsing the Internet. For more advanced stuff and other HTTP methods I would recommend Postman, an extension for Chrome browser. You can easily select methods and headers, edit body, save requests etc. If you are a command line freak, you can grab curl for that. Now let’s hop to the code.

Simple Rest Controller

We will start where we left two weeks ago. Let’s rename our MainController to FrontendController and add the second one like that:

@RestController
@RequestMapping("/api")
public class WebServiceController {

    @RequestMapping("/greeting")
    public Greeting getGreeting() {
        return new Greeting("Hello!");
    }
}

The controller is annotated with @RestController, which is basically a @Controller with @ResponseBody, meaning that the methods return type should be the body of response. The second class-level annotation is @Request Mapping which defines the part of address following application context. The method-level @RequestMapping will add a suffix specific to particular method. Default response body will be in JSON, unless state otherwise in “produces” property of @RequestMapping.

We can now issue a request to http://localhost:8080/SpringAngularIntro/api/greeting and the response should be {“text”:”Hello!”}. From now on, I will omit everything before last slash in request addresses for the sake of brevity.

Parameters

To make things more interesting, we can add parameters. Let’s define second method in our controller:

@RequestMapping("/greetingwithparam")
public Greeting getGreetingWithParam(
    @RequestParam(name = "name", required = false, defaultValue = "stranger")
    String someone) {
    return new Greeting("Hello " + someone + "!");
}

The method takes parameters from the request. We can simply skip @RequestParam annotation, and it will still work, however the annotation lets you indicate whether parameter is optional or not. By default it’s required and if we don’t provide one, there will be an error upon request. If it’s optional, we may provide default value that will be used in absence of parameter in request. We may use other name for method argument and request parameter, for example if we don’t want camel case in our url as opposed to our code.

We can now issue a request: /greetingwithparam?name=world and the response should be: {“text”:”Hello world!”}. On the other hand, if we omit parameter: /greetingwithparam we will get {“text”:”Hello stranger!”}.

Steampunk_spider_by_CatherinetteRings.jpg

Model

One parameter is ok, but from the code perspective, dealing with multiple parameters in method signature is not very elegant, especially if they have more annotations. For example, We might want to validate our inputs using stuff from javax.validation.constraints. Let’s have more parameters and enclose them in separate class, like that:

public class GreetingParams {

	private String title;

	@NotNull
	@Valid
	private String name;

	@NotNull
	@Valid
	@Size(min = 2, max = 50)
	private String surname;

	@Max(value = 130)
	private int age;
}

Meaning of annotations is intuitive. We can assure that parameters won’t be null, have particular length or range of values. We may now provide the class as sole parameter of our next method. To make validating annotations work, we need to add @Validated annotations to it.

@RequestMapping("/greetingwithmodel")
public Greeting getGreetingWithModel(@Validated GreetingParams model) {
    return service.getGreeting(model.getTitle(), model.getName(), model.getSurname());
}

We can now issue a request: /greetingwithmodel?title=mr&name=John&surname=Smith&age=40 and in response we should get {“text”:”Hello mr John Smith!”}.

Path Variables

The previous request did not look very well, right? There is a solution for that called @PathVariable.

@RequestMapping("/greetingwithpath/{title}/{name}/{surname}/{age}")
public Greeting getGreetingWithPath(@PathVariable String title,
    @PathVariable String name,
    @PathVariable String surname,
    @PathVariable Integer age) {
    System.out.println("He's " + age + " years old");
    return service.getGreeting(title, name, surname);
}

We need to provide a template of address in @RequestMapping and annotate our fourth method’s arguments accordingly. Now we can get similar effect as before using much shorter request: /greetingwithpath/mr/John/Smith/40. The order of parameters needs to be preserved now however.

steampunk__alice_in_wonderland_caterpillar_ii_by_catherinetterings-d7fhp95.jpg

Post request

What if we need more than few parameters or more complex data structure as an argument to our web service? Or perhaps a cleaner url? We should go with the POST method and provide request body. Let’s add fifth method:

@RequestMapping(value = "/greetingwithpost", method = RequestMethod.POST)
public Greeting getGreetingWithPost(@RequestBody GreetingParams body) {
    System.out.println("He's " + body.getAge() + " years old");
    return service.getGreeting(body.getTitle(), body.getName(), body.getSurname());
}

The @RequestMapping by default server all HTTP methods, but now we specified it as RequestMethod.POST only. The Java method argument is similar to model, but now annotated with @RequestBody, meaning it should not be constructed from request arguments, but from its body. To provide the body, let’s change method in Postman to POST, add Header with key Content-Type and value application/json and raw body like this: { “title”:”mr”, “name”:”John”,  “surname”:”Smith”, “age”: 40 }. Fire at will at /greetingwithpost and we should get the familiar response.

HTTP status codes

HTTP responses comes with status codes. There are few classes of codes, 2xx are for success, 3xx for redirection, 4xx for client error and 5xx for server error. If we return an arbitrary object from @RestController, and the call is successful, by default it gets a 200 code. Let’s examine other possibilities:

@RequestMapping(value = "/greetingwithcodes", method = RequestMethod.POST)
public ResponseEntity<Greeting> getGreetingWithCodes(@RequestBody GreetingParams body) {
    Greeting greeting = service.getGreeting(body.getTitle(), body.getName(), body.getSurname());
    HttpStatus status = HttpStatus.CREATED;
    System.out.println(body.getSurname());
    if ("Smith".equals(body.getSurname())) {
        status = HttpStatus.ALREADY_REPORTED;
    } else if ("Jackson".equals(body.getSurname())) {
        status = HttpStatus.GONE;
    }
    return new ResponseEntity<Greeting>(greeting, status);
}

Now instead of the naked body, we return a ResponseEntity object that takes the body and the code as argument. We can examine surname, and based on that return different code, depending on our application logic. We had Smith few times, so if we issue our POST request changing only address to /greetingwithcodes, we will get status code 208 which means “Already reported”. If we change surname. It will be 201 “Created”, however if then name will be Michael and surname Jackson, we will get code 410 “Gone”…

Spring is coming?

If you are curious about details of what is happening with your Java object between the point you return it from REST controller and the point it leaves your computer through the network cable, you might want to go through episodes 49 to 52, a series on layers of abstraction in Java web applications. They are a kind of Christmas special episodes, themed with journey from The Hobbit.

If you are interested in more details about API, and how to handle stuff around them, you might want to check episode 47 on API management tools.

Uff… That was a long one! In the next episode, we will continue with Spring basics, and the topic will be: how to talk to the database. See you!

how_to_train_your_dragon_screencap___toothless_by_sdk2k9-d5grw98

Images source: CatherinetteRings

 
4 Comments

Posted by on March 9, 2017 in API, Spring, Technology

 

Tags: , , , , ,

4 responses to “Spring Web Basics

  1. Mateusz Nowak

    April 8, 2017 at 12:30 pm

    Hello,
    very nice article, It’s help me a lot to remind content form lectures, and understand better.
    But I have one notice about that.
    In the post you’ve written that: “The @RequestMapping default HTTP method is GET”, but i think you’re wrong here. As I have been learning Spring, I’ve read that there is no default method, te @RequestMapping annotation maps for all HTTP methods. But I can always be wrong. Please verify that, and if it’s necesassary correct it.

    And also could you explain me why do you use @Valid addnotation? What’s mean? Beacause of I was testing the programm without it, and it worked the same.

    See you on the next our PWr course meeting ;)

    PS. You have small typo in the article (but you know is programming is very important), you wrote: ”
    Let’s rename our MainController to FrontendController and add the second one like that”, but the name in code is WebServiceController.

    Like

     
  2. gvaireth

    April 8, 2017 at 2:38 pm

    Thanks for comments.

    Regarding GET being default, you are correct, there is a mistake in the article.

    @Valid is needed here to enable validations from GreetingsParam class. If you use a one-letter surname as an argument without @Valid annotation, error will not be reported.

    FrontendController refers to a class that was originally called MainController. WebServiceController is a new one.

    Good luck on the exam ;)

    Like

     

Leave a comment