You will use a simple approach to building a stand-alone application. It packages everything into a single, executable JAR file, driven by a main()
method. Along the way, you use Spring's support for embedding the Jetty servlet container as the HTTP runtime, instead of deploying it to an external instance. Therefore, we would create the executable JAR in place of the war that needs to be deployed on external web servers.
Now, as you are ready with Spring Boot in NetBeans IDE, you could create your sample web service. You will create a Math API that performs simple calculations and generates the result as JSON.
Let's discuss how we can call and get responses from REST services.
The service will handle GET requests for /calculation/sqrt or /calculation/power and so on. The GET request should return a 200 OK
response with JSON in the body that represents the square root of given number. It should look something like this:
{ "function": "sqrt", "input": [ "144" ], "output": [ "12.0" ] }
The input
field is the input parameter for the square root function, and the content is the textual representation of the result.
You could create a resource representation class to model the representation by using Plain Old Java Object (POJO) with fields, constructors, setters, and getters for the input, output, and function data:
package com.packtpub.mmj.restsample.model; import java.util.List; public class Calculation { String function; private List<String> input; private List<String> output; public Calculation(List<String> input, List<String> output, String function) { this.function = function; this.input = input; this.output = output; } public List<String> getInput() { return input; } public void setInput(List<String> input) { this.input = input; } public List<String> getOutput() { return output; } public void setOutput(List<String> output) { this.output = output; } public String getFunction() { return function; } public void setFunction(String function) { this.function = function; } }
Roy Fielding defined and introduced the term REST, Representational State Transfer in his doctoral dissertation. REST is a style of software architecture for a distributed hypermedia system such as WWW. RESTful refers to those systems that conform to REST architecture properties, principles, and constraints.
Now, you'll create a REST controller to handle the calculation resource. The controller handles the HTTP requests in the Spring RESTful web service implementation.
@RestController
is a class-level annotation used for the resource
class introduced in Spring 4. It is a combination of @Controller
and @ResponseBody
, and because of it, class returns a domain object instead of a view.
In the following code, you can see that the CalculationController
class handles GET requests for /calculation
by returning a new instance of the calculation
class.
We will implement two URLs for a calculation resource – the square root (Math.sqrt()
function) as /calculations/sqrt
URL, and power (Math.pow()
function) as /calculation/power
URL.
@RequestMapping
annotation is used at class-level to map the /calculation
URI to CalculationController
class that is, it ensures that the HTTP request to /calculation
is mapped to the CalculationController
class. Based on the path defined using the annotation @RequestMapping
of the URI (postfix of /calculation
, for example, /calculation/sqrt/144
), it would be mapped to respective methods. Here, the request mapping /calculation/sqrt
is mapped to the sqrt()
method and /calculation/power
is mapped to the pow()
method.
You might have also observed that we have not defined what request method (GET/POST/PUT, and so on) these methods would use. The @RequestMapping
annotation maps all the HTTP request methods by default. You could use specific methods by using the method property of RequestMapping
. For example, you could write a @RequestMethod
annotation in the following way to use the POST
method:
@RequestMapping(value = "/power", method = POST)
For passing the parameters along the way, the sample demonstrates both request parameters and path parameters using annotations @RequestParam
and @PathVariable
respectively.
@RequestParam
is responsible for binding the query parameter to the parameter of the controller's method. For example, the QueryParam base and exponent are bound to parameters b
and e
of method pow()
of CalculationController
respectively. Both of the query parameters of the pow()
method are required since we are not using any default value for them. Default values for query parameters could be set using the defaultValue
property of @RequestParam
for example @RequestParam(value="base", defaultValue="2")
, here, if the user does not pass the query parameter base, then the default value 2
would be used for the base.
If no defaultValue
is defined, and the user doesn't provide the request parameter, then RestController
returns the HTTP status code 400 with the message 400 Required String parameter base is not present. It always uses the reference of the first required parameter if more than one of the request parameters is missing:
{ "timestamp": 1464678493402, "status": 400, "error": "Bad Request", "exception": "org.springframework.web.bind.MissingServletRequestParameterException", "message": "Required String parameter 'base' is not present", "path": "/calculation/power/" }
@PathVariable
helps you to create the dynamic URIs. @PathVariable
annotation allows you to map Java parameters to a path parameter. It works with @RequestMapping
where placeholder is created in URI then the same placeholder name is used either as a PathVariable
or a method parameter, as you can see in the CalculationController
class's method sqrt()
. Here, the value placeholder is created inside the @RequestMapping
and the same value is assigned to the value of the @PathVariable
.
Method sqrt()
takes the parameter in the URI in place of the request parameter. For example, http://localhost:8080/calculation/sqrt/144
. Here, the 144
value is passed as the path parameter and this URL should return the square root of 144 that is, 12
.
To use the basic check in place, we use the regular expression "^-?+\d+\.?+\d*$"
to allow only valid numbers in parameters. If non-numeric values are passed, the respective method adds an error message in the output key of the JSON.
CalculationController
also uses the regular expression .+
in the path
variable (path
parameter) to allow the decimal point(.
) in numeric values - /path/{variable:.+}
. Spring ignores anything after the last dot. Spring default behavior takes it as a file extension.
There are other alternatives such as adding a slash at the end (/path/{variable}/
) or overriding the method configurePathMatch()
of WebMvcConfigurerAdapter
by setting the useRegisteredSuffixPatternMatch
to true
, using PathMatchConfigurer
(available in Spring 4.0.1+).
package com.packtpub.mmj.restsample.resources; package com.packtpub.mmj.restsample.resources; import com.packtpub.mmj.restsample.model.Calculation; import java.util.ArrayList; import java.util.List; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import static org.springframework.web.bind.annotation.RequestMethod.GET; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/calculation") public class CalculationController { private static final String PATTERN = "^-?+\d+\.?+\d*$"; @RequestMapping("/power") public Calculation pow(@RequestParam(value = "base") String b, @RequestParam(value = "exponent") String e) { List<String> input = new ArrayList(); input.add(b); input.add(e); List<String> output = new ArrayList(); String powValue = ""; if (b != null && e != null && b.matches(PATTERN) && e.matches(PATTERN)) { powValue = String.valueOf(Math.pow(Double.valueOf(b), Double.valueOf(e))); } else { powValue = "Base or/and Exponent is/are not set to numeric value."; } output.add(powValue); return new Calculation(input, output, "power"); } @RequestMapping(value = "/sqrt/{value:.+}", method = GET) public Calculation sqrt(@PathVariable(value = "value") String aValue) { List<String> input = new ArrayList(); input.add(aValue); List<String> output = new ArrayList(); String sqrtValue = ""; if (aValue != null && aValue.matches(PATTERN)) { sqrtValue = String.valueOf(Math.sqrt(Double.valueOf(aValue))); } else { sqrtValue = "Input value is not set to numeric value."; } output.add(sqrtValue); return new Calculation(input, output, "sqrt"); } }
Here, we are exposing only the power
and sqrt
functions for the Calculation
resource using URI /calculation/power
and /calculation/sqrt
.
One interesting thing here is that due to Spring's HTTP message converter support, the Calculation
object gets converted to JSON automatically. You don't need to do this conversion manually. If Jackson 2 is on the classpath, Spring's MappingJackson2HttpMessageConverter
converts the Calculation
object to JSON.
Create a class RestSampleApp
with the annotation SpringBootApplication
. The main()
method uses Spring Boot's SpringApplication.run()
method to launch an application.
We will annotate the RestSampleApp
class with the @SpringBootApplication
that adds all of the following tags implicitly:
@Configuration
annotation tags the class as a source of Bean definitions for the application context.@EnableAutoConfiguration
annotation indicates that Spring Boot is to start adding beans based on classpath settings, other beans, and various property settings.@EnableWebMvc
annotation is added if Spring Boot finds the spring-webmvc on the classpath. It treats the application as a web application and activates key behaviors such as setting up a DispatcherServlet.@ComponentScan
annotation tells Spring to look for other components, configurations, and services in the given package:package com.packtpub.mmj.restsample; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RestSampleApp { public static void main(String[] args) { SpringApplication.run(RestSampleApp.class, args); } }
This web application is 100 percent pure Java and you didn't have to deal with configuring any plumbing or infrastructure using XML; instead it uses the Java annotation, that is made even simpler by Spring Boot. Therefore, there wasn't a single line of XML except pom.xml
for Maven. There wasn't even a web.xml
file.