0% found this document useful (0 votes)
45 views30 pages

Unit - V Spring REST

Uploaded by

m.saimohan2020
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
45 views30 pages

Unit - V Spring REST

Uploaded by

m.saimohan2020
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

@PathVariable:

The @PathVariable method parameter annotation is used to indicate that a method parameter should be bound
to the value of a URI template variable.

The following code snippet shows the use of a single @PathVariable in a controller method:

@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}

The URI Template "/owners/{ownerId}" specifies the variable name ownerId. When the controller
handles this request, the value of ownerId is set the value in the request URI. For example, when a request
comes in for /owners/fred, the value 'fred' is bound to the method parameter String ownerId.

The matching of method parameter names to URI Template variable names can only be done if your code
is compiled with debugging enabled. If you do have not debugging enabled, you must specify the name of
the URI Template variable name to bind to in the @PathVariable annotation.

For example

@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String ownerId, Model model)
{
// implementation omitted
}

The name of the method parameter does not matter in this case, so you may also use a controller method
with the signature shown below

@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model
model) {
// implementation omitted
}

Multiple @PathVariable annotations can be used to bind to multiple URI Template variables as shown
below:

@RequestMapping("/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(@PathVariable String ownerId, @PathVariable String
petId, Model model) {
Owner owner = ownerService.findOwner(ownderId);
Pet pet = owner.getPet(petId);
model.addAttribute("pet", pet);
return "displayPet";
}

The following code snippet shows the use of path variables on a relative path

@Controller
@RequestMapping("/owners/{ownerId}/**")
public class RelativePathUriTemplateController {

@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String
petId, Model model) {
// implementation omitted
}
}

@RequestParam :@RequestParam annotation is used to read the form data and bind it automatically to
the parameter present in the provided method. So, it ignores the requirement of HttpServletRequest object
to read the provided data.

Including form data, it also maps the request parameter to query parameter and parts in multipart requests.
If the method parameter type is Map and a request parameter name is specified, then the request
parameter value is converted to a Map else the map parameter is populated with all request parameter
names and values.

RequestParam Example

Let's create a login page that contains a username and password. Here, we validate the password with a
specific value.

1. Add dependencies to pom.xml

1. <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->


2. <dependency>
3. <groupId>org.springframework</groupId>
4. <artifactId>spring-webmvc</artifactId>
5. <version>5.1.1.RELEASE</version>
6. </dependency>
7.
8. <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
9. <dependency>
10. <groupId>javax.servlet</groupId>
11. <artifactId>servlet-api</artifactId>
12. <version>3.0-alpha-1</version>
13. </dependency>

2. Create the request page

It is the login page that receive name and password from the user.

index.jsp

1. <html>
2. <body>
3. <form action="hello">
4. UserName : <input type="text" name="name"/> <br><br>
5. Password : <input type="text" name="pass"/> <br><br>
6. <input type="submit" name="submit">
7. </form>
8. </body>
9. </html>

3. Create the Controller Class

In controller class:

The @RequestParam is used to read the HTML form data provided by a user and bind it to the request
parameter.

The Model contains the request data and provides it to view page.

HelloController.java

1. package com.javatpoint;
2. import org.springframework.stereotype.Controller;
3. import org.springframework.ui.Model;
4. import org.springframework.web.bind.annotation.RequestMapping;
5. import org.springframework.web.bind.annotation.RequestParam;
6.
7. @Controller
8. public class HelloController {
9.
10. @RequestMapping("/hello")
11. //read the provided form data
12. public String display(@RequestParam("name") String name,@RequestParam("pass") String pa
ss,Model m)
13. {
14. if(pass.equals("admin"))
15. {
16. String msg="Hello "+ name;
17. //add a message to the model
18. m.addAttribute("message", msg);
19. return "viewpage";
20. }
21. else
22. {
23. String msg="Sorry "+ name+". You entered an incorrect password";
24. m.addAttribute("message", msg);
25. return "errorpage";
26. }
27. }
28. }

4. Create the other view components

To run this example, the following view components must be located inside the WEB-INF/jsp directory.

viewpage.jsp

1. <html>
2. <body>
3. ${message}
4. </body>
5. </html>

errorpage.jsp

1. <html>
2. <body>
3. ${message}
4. <br><br>
5. <jsp:include page="/index.jsp"></jsp:include>
6. </body>
7. </html>
8. </html>

Output:
Matrix variables is a spring coined term and an alternative implementation for passing and parsing URI
path parameters.
Matrix variables support became available in Spring MVC 3.2 and is meant to simplify requests with a
large number of parameters.
In this article, we will show how we can simplify complex GET requests that use either variable or
optional path parameters inside the different path segments of a URI.

2. Configuration

To enable Spring MVC Matrix Variables, let’s start with the configuration:
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}Copy

Otherwise, they’re disabled by default.

3. How to Use Matrix Variables

These variables can appear in any part of the path, and the character equals (“=”) is used for giving values
and the semicolon(‘;’) for delimiting each matrix variable. On the same path, we can also repeat the same
variable name or separate different values using the character comma(‘,’).

Our example has a controller that provides information about the employees. Each employee has a
working area, and we can search by that attribute. The following request could be used for searching:

http://localhost:8080/spring-mvc-java-
2/employeeArea/workingArea=rh,informatics,adminCopy
or like this:

http://localhost:8080/spring-mvc-java-2
/employeeArea/workingArea=rh;workingArea=informatics;workingArea=adminCopy

When we want to refer to these variables in Spring MVC, we should use the
annotation @MatrixVariable.

In our examples, we will use the Employee class:


public class Employee {

private long id;


private String name;
private String contactNumber;

// standard setters and getters


}Copy
And also the Company class:
public class Company {

private long id;


private String name;

// standard setters and getters


}Copy
These two classes will bind the request parameters.

4. Defining Matrix Variable Properties

We can specify required or default properties for the variable. In the following example,
the contactNumber is required, so it must be included in our path, something like this :
http://localhost:8080/spring-mvc-java-
2/employeesContacts/contactNumber=223334411Copy

The request will be handled by the following method:


@RequestMapping(value = "/employeesContacts/{contactNumber}",
method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<List<Employee>> getEmployeeByContactNumber(
@MatrixVariable(required = true) String contactNumber) {
List<Employee> employeesList = new ArrayList<Employee>();
...
return new ResponseEntity<List<Employee>>(employeesList, HttpStatus.OK);
}Copy
As a result, we will get all the employees who have the contact number 223334411.

5. Complement Parameter

Matrix variables can complement path variables.


For example, we are searching an employee for his/her name, but we can also include the starting
numbers of his/her contact number.
The request for this search should be like this:
http://localhost:8080/spring-mvc-java-
2/employees/John;beginContactNumber=22001Copy
The request will be handled by the following method:
@RequestMapping(value = "/employees/{name}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<List<Employee>> getEmployeeByNameAndBeginContactNumber(
@PathVariable String name, @MatrixVariable String beginContactNumber) {
List<Employee> employeesList = new ArrayList<Employee>();
...
return new ResponseEntity<>(employeesList, HttpStatus.OK);
}Copy

As a result, we will get all the employees who have the contact number 22001 or whose name is John.

6. Binding All Matrix Variables

If for some reason, we want to get all the variables that are available on the path, we can bind them to
a Map:
http://localhost:8080/spring-mvc-java-
2/employeeData/id=1;name=John;contactNumber=2200112334Copy

This request will be handled by the following method:


@GetMapping("employeeData/{employee}")
@ResponseBody
public ResponseEntity<Map<String, String>> getEmployeeData(
@MatrixVariable Map<String, String> matrixVars) {
return new ResponseEntity<>(matrixVars, HttpStatus.OK);
}Copy
Of course, we can restrict binding to the matrix variables of a specific part of the path. For example, if we
have a request like this:
http://localhost:8080/spring-mvc-java-2/
companyEmployee/id=2;name=Xpto/employeeData/id=1;name=John;
contactNumber=2200112334Copy
And we only want to get all the variables that belong to employeeData; then we should use as an input
parameter this:
@RequestMapping(
value = "/companyEmployee/{company}/employeeData/{employee}",
method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Map<String, String>> getEmployeeDataFromCompany(
@MatrixVariable(pathVar = "employee") Map<String, String> matrixVars) {
...
}Copy

7. Partial Binding

Apart from simplicity, flexibility is another gain, matrix variables can be used in a variety of different
ways. For example, we can get each variable from each path segment. Consider the following request:
http://localhost:8080/spring-mvc-java-2/
companyData/id=2;name=Xpto/employeeData/id=1;name=John;
contactNumber=2200112334Copy

If we only want to know the matrix variable name of the companyData segment, then, we should use as
an input parameter the following:
@MatrixVariable(value="name", pathVar="company") String name
Copy

8. Firewall Setup

If the application uses Spring Security, then StrictHttpFirewall is used by default. This blocks requests
that appear to be malicious, including Matrix Variables with semicolon separator.
We can customize this implementation in application configuration and allow such variables whilst
rejecting other possibly malicious requests.
However, this way, we can open the application to attacks. Therefore, we should only implement this
after careful analysis of the application and security requirements.

9. Conclusion

This article illustrated some of the various ways that matrix variables can be used.
It’s essential to understand how this new tool can deal with requests that are too complex or help us add
more parameters to delimit our search.

Exception Handling

The Spring framework MVC module has excellent features for error handling. But it is left to the
developer to use those features to treat the exceptions and return meaningful responses to the API client.

Let’s look at an example of the default Spring Boot answer when we issue an HTTP POST to
the /birds endpoint with the following JSON object that has the string “aaa” on the field “mass,” which
should be expecting an integer:
{
"scientificName": "Common blackbird",
"specie": "Turdus merula",
"mass": "aaa",
"length": 4
}
The Spring Boot default answer, without proper error handling, looks like this:

{
"timestamp": 1658551020,
"status": 400,
"error": "Bad Request",
"exception":
"org.springframework.http.converter.HttpMessageNotReadableException",
"message": "JSON parse error: Unrecognized token 'three': was expecting
('true', 'false' or 'null'); nested exception is
com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'aaa': was
expecting ('true', 'false' or 'null')\n at [Source:
java.io.PushbackInputStream@cba7ebc; line: 4, column: 17]",
"path": "/birds"
}

The Spring Boot DefaultErrorAttributes-generated response has some good fields, but it is too
focused on the exception. The timestamp field is an integer that doesn’t carry information about its
measurement unit. The exception field is only valuable to Java developers, and the message leaves the
API consumer lost in implementation details that are irrelevant to them. What if there were more details
we could extract from the exception? Let’s learn how to handle exceptions in Spring Boot properly and
wrap them into a better JSON representation to make life easier for our API clients.
As we’ll be using Java date and time classes, we first need to add a Maven dependency for the Jackson
JSR310 converters. They convert Java date and time classes to JSON representation using
the @JsonFormat annotation:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Next, let’s define a class for representing API errors. We’ll create a class called ApiError with enough
fields to hold relevant information about errors during REST calls:
class ApiError {

private HttpStatus status;


@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy
hh:mm:ss")
private LocalDateTime timestamp;
private String message;
private String debugMessage;
private List<ApiSubError> subErrors;
private ApiError() {
timestamp = LocalDateTime.now();
}

ApiError(HttpStatus status) {
this();
this.status = status;
}

ApiError(HttpStatus status, Throwable ex) {


this();
this.status = status;
this.message = "Unexpected error";
this.debugMessage = ex.getLocalizedMessage();
}

ApiError(HttpStatus status, String message, Throwable ex) {


this();
this.status = status;
this.message = message;
this.debugMessage = ex.getLocalizedMessage();
}
}

 The status property holds the operation call status, which will be anything from 4xx to signal
client errors or 5xx to signal server errors. A typical scenario is an HTTP code 400:
BAD_REQUEST when the client, for example, sends an improperly formatted field, like an
invalid email address.
 The timestamp property holds the date-time instance when the error happened.
 The message property holds a user-friendly message about the error.
 The debugMessage property holds a system message describing the error in detail.
 The subErrors property holds an array of suberrors when there are multiple errors in a single
call. An example would be numerous validation errors in which multiple fields have failed.
The ApiSubError class encapsulates this information:
abstract class ApiSubError {

@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
class ApiValidationError extends ApiSubError {
private String object;
private String field;
private Object rejectedValue;
private String message;

ApiValidationError(String object, String message) {


this.object = object;
this.message = message;
}
}

The ApiValidationError is a class that extends ApiSubError and expresses validation problems
encountered during the REST call.
Below, you’ll see examples of JSON responses generated after implementing these improvements.

Here is a JSON example returned for a missing entity while calling endpoint GET /birds/2:
{
"apierror": {
"status": "NOT_FOUND",
"timestamp": "22-07-2022 06:20:19",
"message": "Bird was not found for parameters {id=2}"
}
}

Here is another example of JSON returned when issuing a POST /birds call with an invalid value for
the bird’s mass:
{
"apierror": {
"status": "BAD_REQUEST",
"timestamp": "22-07-2022 06:49:25",
"message": "Validation errors",
"subErrors": [
{
"object": "bird",
"field": "mass",
"rejectedValue": 999999,
"message": "must be less or equal to 104000"
}
]
}
}
Spring Boot Error Handler

Let’s explore some Spring annotations used to handle exceptions.

RestController is the base annotation for classes that handle REST operations.

ExceptionHandler is a Spring annotation that provides a mechanism to treat exceptions thrown during

execution of handlers (controller operations). This annotation, if used on methods of controller classes,
will serve as the entry point for handling exceptions thrown within this controller only.
Altogether, the most common implementation is to use @ExceptionHandler on methods
of @ControllerAdvice classes so that the Spring Boot exception handling will be applied globally or
to a subset of controllers.
ControllerAdvice is an annotation in Spring and, as the name suggests, is “advice” for multiple

controllers. It enables the application of a single ExceptionHandler to multiple controllers. With this
annotation, we can define how to treat such an exception in a single place, and the system will call this
handler for thrown exceptions on classes covered by this ControllerAdvice.
The subset of controllers affected can be defined by using the following selectors
on @ControllerAdvice: annotations(), basePackageClasses(),
and basePackages(). ControllerAdvice is applied globally to all controllers if no selectors are
provided
By using @ExceptionHandler and @ControllerAdvice, we’ll be able to define a central point for
treating exceptions and wrapping them in an ApiError object with better organization than is possible
with the default Spring Boot error-handling mechanism.
Handling Exceptions:

Next, we’ll create the class that will handle the exceptions. For simplicity, we call
it RestExceptionHandler, which must extend from Spring
Boot’s ResponseEntityExceptionHandler. We’ll be
extending ResponseEntityExceptionHandler, as it already provides some basic handling of Spring
MVC exceptions. We’ll add handlers for new exceptions while improving the existing ones.

Overriding Exceptions Handled in ResponseEntityExceptionHandler

If you take a look at the source code of ResponseEntityExceptionHandler, you’ll see a lot of
methods called handle******(),
like handleHttpMessageNotReadable() or handleHttpMessageNotWritable(). Let’s see how
can we extend handleHttpMessageNotReadable() to
handle HttpMessageNotReadableException exceptions. We just have to override the
method handleHttpMessageNotReadable() in our RestExceptionHandler class:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object>
handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders
headers, HttpStatus status, WebRequest request) {
String error = "Malformed JSON request";
return buildResponseEntity(new ApiError(HttpStatus.BAD_REQUEST, error, ex));
}

private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {


return new ResponseEntity<>(apiError, apiError.getStatus());
}

//other exception handlers below

We have declared that in case of a thrownHttpMessageNotReadableException, the error message


will be “Malformed JSON request” and the error will be encapsulated in the ApiError object. Below, we
can see the answer of a REST call with this new method overridden:
{
"apierror": {
"status": "BAD_REQUEST",
"timestamp": "22-07-2022 03:53:39",
"message": "Malformed JSON request",
"debugMessage": "JSON parse error: Unrecognized token 'aaa': was expecting
('true', 'false' or 'null'); nested exception is
com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'aaa': was
expecting ('true', 'false' or 'null')\n at [Source:
java.io.PushbackInputStream@7b5e8d8a; line: 4, column: 17]"
}
}

Implementing Custom Exceptions


Next, we’ll create a method that handles an exception not yet declared inside Spring
Boot’s ResponseEntityExceptionHandler.
A common scenario for a Spring application that handles database calls is to provide a method that
returns a record by its ID using a repository class. But if we look into
the CrudRepository.findOne() method, we’ll see that it returns null for an unknown object. If our
service calls this method and returns directly to the controller, we’ll get an HTTP code 200 (OK) even if
the resource isn’t found. In fact, the proper approach is to return a HTTP code 404 (NOT FOUND) as
specified in the HTTP/1.1 spec.
We’ll create a custom exception called EntityNotFoundException to handle this case. This one is
different from javax.persistence.EntityNotFoundException, as it provides some constructors
that ease the object creation, and one may choose to handle the javax.persistence exception
differently.

That said, let’s create an ExceptionHandler for this newly created EntityNotFoundException in
our RestExceptionHandler class. Create a method called handleEntityNotFound() and annotate
it with @ExceptionHandler, passing the class object EntityNotFoundException.class to it. This
declaration signalizes Spring that every time EntityNotFoundException is thrown, Spring should call
this method to handle it.
When annotating a method with @ExceptionHandler, a wide range of auto-injected parameters
like WebRequest, Locale, and others may be specified as described here. We’ll provide the
exception EntityNotFoundException as a parameter for this handleEntityNotFound method:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

//other exception handlers

@ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFound(
EntityNotFoundException ex) {
ApiError apiError = new ApiError(NOT_FOUND);
apiError.setMessage(ex.getMessage());
return buildResponseEntity(apiError);
}
}
Great! In the handleEntityNotFound() method, we set the HTTP status code to NOT_FOUND and
usethe new exception message. Here is what the response for the GET /birds/2 endpoint looks like
now:
{
"apierror": {
"status": "NOT_FOUND",
"timestamp": "22-07-2022 04:02:22",
"message": "Bird was not found for parameters {id=2}"
}
}

The Importance of Spring Boot Exception Handling

It is important to control exception handling so we can properly map exceptions to the ApiError object

and inform API clients appropriately. Additionally, we would need to create more handler methods (the

ones with @ExceptionHandler) for thrown exceptions within the application code. The GitHub

code provides more more examples for other common exceptions

like MethodArgumentTypeMismatchException, ConstraintViolationException.

Data Validation:

When building REST API, we expect RESTful service to have a certain structure or format. We expect
REST API request to follow this structure and constraints. We can not assume that API request will
follow structure and constraints. How to handle if REST request is not meeting constraints.

We need to think about following questions while designing our REST API.

 How should I validate REST API data?


 What message should I send if data validation fails?

One of the fundamental principles of RESTful services is to think about “Consumers” while designing
your REST web services. We should keep in mind below design principles for validating REST API in
Spring.

 REST API should return a clear message indicating what was wrong int he request.
 API should try to provide information on what can be done to fix the error.
 Well defined response status.

Spring MVC provides a number of build in options for REST API date validation. We will cover different
options in this post. It is up to your requirement to determine which approach fulfill your use case.

1. Simple Spring MVC Validation

If our REST API is using @RequestParam or @PathVaraible, Spring provides out of the box support for
validating it. Here is a simple use case for validating REST data using Spring

@GetMapping("/greeting")

public Greeting sayHello(@RequestParam(value = "name") String name){

return new Greeting(1, "Hello "+name);

This is the simplest validation provided by Spring MVC. It will validate incoming request. Spring will
send a detailed message back to the consumer in case we are missing required data in the REST API.

If we run our application without passing name in the request parameter, Spring will send a detailed error
response back to the consumer.

"timestamp": 1517983914615,

"status": 400,

"error": "Bad Request",

"exception": "org.springframework.web.bind.MissingServletRequestParameterException",

"message": "Required String parameter 'name' is not present",

"path": "/greeting"
}

We will talk about customizing error response in our exception handling post.

2. REST Validation using Bean Validation (JSR303)

Real life REST API’s are more complex than a simple method with few parameters. Enterprise REST
API involve complex domain models and also require the ability to validate REST API data.

Spring MVC provides build in support for JSR303 (Bean Validation). Bean Validation comes with an
extensive list of validation available out of the box along with the ability to create custom validations.

With Spring Bean Validation support, we need to annotate out DTO with required annotations and build
in validation support will ensure to validate incoming data before passing it to our business layer.

For the list of build in validation, please refer to Hibernate Validator.

Here is our sample customer DTO

public class Customer {

@NotNull(message = "Please provide first Name")

private String firstName;

@NotNull(message = "Please provide last Name")

private String lastName;

@NotNull

@Min(value = 18, message = "Age should be equal or more than 18")

private int age;


@Email(message = "Please provide valid email address")

private String email;

public String getFirstName() {

return firstName;

public void setFirstName(String firstName) {

this.firstName = firstName;

public String getLastName() {

return lastName;

public void setLastName(String lastName) {

this.lastName = lastName;

public int getAge() {


return age;

public void setAge(int age) {

this.age = age;

public String getEmail() {

return email;

public void setEmail(String email) {

this.email = email;

before we move ahead, let’s discuss what we are trying to validate in our DTO

 We will not accept null values for firstName and lastName (You can also
add @notEmpty annotation from Hibernate validator)
 Age should not be null and should be greater than or equal to 18.
 We need a valid email address for the customer.
Bean Validation provides us a way not only to validate REST API request but also provide consumer-
centric error messages.
We are using the hard-coded message in the class itself, but Spring provides the way to use a localized
properties file for localized messages.

@RestController

@RequestMapping("/api/rest")

public class CustomerController {

@PostMapping("/customer")

public ResponseEntity<?> createCustomer(@Valid @RequestBody Customer customer){

HttpHeaders responseHeader = new HttpHeaders();

return new ResponseEntity<>(customer, responseHeader, HttpStatus.CREATED);

If we run our code, with following input

{"firstName":"Umesh","lastName":null,"age":"18","email":"umeshawasthi@wordpress-
241348-2978695.cloudwaysapps.com"}

We will get following response back from our REST API.

timestamp": 1518066946111,

"status": 400,

"error": "Bad Request",

"exception": "org.springframework.web.bind.MethodArgumentNotValidException",

"errors": [],
"message": "Validation failed for object='customer'. Error count: 1",

"path": "/api/rest/customer"

If we will send all values in correct format and data type, REST API will send success response back to
the consumer

<HTTP : 201 Created

"firstName": "Umesh",

"lastName": "Awasthi",

"age": 18,

"email": "[email protected]"

3. Custom Spring Validator

JSR 303 bean validation provides a number of out of the box validator. For enterprise applications, we
will get into a situation where out of the box validator will not fulfill our business requirements.

Spring provides flexibility to create our own custom validator and uses these custom validations. Please
read Spring MVC Custom Validator for more detail.

You can use same domain object for different endpoints. Let’s take the example of our customer.

If we are creating a new customer, we do not need customer id and it can be null.

For updating customer, we need customer id to identify customer for the update.

For such use cases, you don’t need 2 separate objects but can use Grouping constraints feature provided
by JSR 303.
Summary

we discussed different options for Validating REST API using Spring. Validating data is essential for
the Spring REST API. Data validation perform data sanity and provide additional security check for data
insertion.

We discussed build in validation support from Spring and Spring MVC along with an option to create
customer validator in Spring if out of the box validator is not sufficient for the business needs.

In our next articles on building REST API with Spring, we will discuss REST Resource Naming Best
Practices.
Securing Spring REST endpoints

Security plays a vital role in REST API development. An insecure REST API can provide direct access to
sensitive data on back-end systems. So organizations need to pay attention to API Security.

Spring Security provides various mechanisms to secure our REST APIs. One of them is API keys.
An API key is a token that a client provides when invoking API calls.

In this tutorial, we’ll discuss the implementation of API key-based authentication in Spring Security.

2. REST API Security

Spring Security can be used to secure REST APIs. REST APIs are stateless. Thus, they shouldn’t use
sessions or cookies.

Instead, these should be secured using Basic authentication, API Keys, JWT, or OAuth2-based
tokens.

2.1. Basic Authentication

Basic authentication is a simple authentication scheme. The client sends HTTP requests with
the Authorization header that contains the word Basic, followed by a space, and a Base64-
encoded string username:password. Basic authentication is only considered secure with other security
mechanisms, such as HTTPS/SSL.

2.2. OAuth2

OAuth2 is the de facto standard for REST API security. It’s an open authentication and authorization
standard that allows resource owners to give clients delegated access to private data via an access token.
2.3. API Keys
Some REST APIs use API keys for authentication. An API key is a token that identifies the API client to
the API without referencing an actual user. The token can be sent in the query string or as a request
header. Like Basic authentication, it’s possible to hide the key using SSL.

In this article, we’ll focus on implementing API Key authentication using Spring Security.

3. Securing REST APIs With API Keys

In this section, we’ll create a Spring Boot application and secure it using API key-based authentication.
3.1. Maven Dependencies

Let’s start by declaring the spring-boot-starter-security dependency in our pom.xml:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Copy
3.2. Creating Custom Filter

The idea is to get the HTTP API Key header from the request, and then check the secret with our
configuration. In this case, we’ll need to add a custom Filter in the Spring Security configuration
class. We’ll start by implementing the GenericFilterBean.

The GenericFilterBean is a simple javax.servlet.Filter implementation that’s Spring-aware. Now let’s


create the AuthenticationFilter class:

public class AuthenticationFilter extends GenericFilterBean {

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
try {
Authentication authentication =
AuthenticationService.getAuthentication((HttpServletRequest) request);

SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception exp) {
HttpServletResponse httpResponse = (HttpServletResponse)
response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
PrintWriter writer = httpResponse.getWriter();
writer.print(exp.getMessage());
writer.flush();
writer.close();
}

filterChain.doFilter(request, response);
}
}Copy
We only need to implement a doFilter() method. In this method, we’ll evaluate the API Key header, and
set the resulting Authentication object into the current SecurityContext instance. Next, the request is
passed to the remaining filters for processing, and then routed to DispatcherServlet, and finally to our
controller.

We’ll delegate the evaluation of the API Key and construction of the Authentication object to
the AuthenticationService class:

public class AuthenticationService {

private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY";


private static final String AUTH_TOKEN = "Baeldung";

public static Authentication getAuthentication(HttpServletRequest


request) {
String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME);
if (apiKey == null || !apiKey.equals(AUTH_TOKEN)) {
throw new BadCredentialsException("Invalid API Key");
}

return new ApiKeyAuthentication(apiKey,


AuthorityUtils.NO_AUTHORITIES);
}
}Copy
Here, we’ll check whether the request contains the API Key header with a secret or not. If the header
is null or isn’t equal to secret, we’ll throw a BadCredentialsException. If the request has the header, it
performs the authentication, adds the secret to the security context, and then passes the call to the next
security filter.

Our getAuthentication method is quite simple; we just compare the API Key header and secret with a
static value. To construct the Authentication object, we must use the same approach Spring Security
typically uses to build the object on a standard authentication.

So let’s extend the AbstractAuthenticationToken class and manually trigger authentication.

3.3. Extending AbstractAuthenticationToken

To successfully implement authentication for our application, we’ll need to convert the incoming
API Key to an Authentication object, such as
an AbstractAuthenticationToken. The AbstractAuthenticationToken class implements
the Authentication interface, representing the secret/principal for an authenticated request.

Let’s create the ApiKeyAuthentication class:

public class ApiKeyAuthentication extends AbstractAuthenticationToken {


private final String apiKey;

public ApiKeyAuthentication(String apiKey, Collection<? extends


GrantedAuthority> authorities) {
super(authorities);
this.apiKey = apiKey;
setAuthenticated(true);
}

@Override
public Object getCredentials() {
return null;
}

@Override
public Object getPrincipal() {
return apiKey;
}
}Copy
The ApiKeyAuthentication class is a type of AbstractAuthenticationToken object with
the apiKey information obtained from the HTTP request. We’ll use the setAuthenticated(true) method in
the construction.

As a result, the Authentication object contains apiKey and authenticated fields:

3.4. Security Config

We can register our custom filter programmatically by creating a SecurityFilterChain bean. In this
case, we’ll need to add the AuthenticationFilter before
the UsernamePasswordAuthenticationFilter class using the addFilterBefore() method on
an HttpSecurity instance.

Let’s create the SecurityConfig class:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws
Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new AuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);

return http.build();
}

}Copy
Also, the session policy is set to STATELESS because we’ll use REST endpoints.

3.5. ResourceController

Finally, we’ll create the ResourceController with a /home mapping:

@RestController
public class ResourceController {
@GetMapping("/home")
public String homeEndpoint() {
return "Baeldung !";
}
}Copy
3.6. Disabling the Default Auto-Configuration

We’ll need to discard the security auto-configuration. To do this, we’ll exclude


the SecurityAutoConfiguration and UserDetailsServiceAutoConfiguration classes:

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class})
public class ApiKeySecretAuthApplication {

public static void main(String[] args) {


SpringApplication.run(ApiKeySecretAuthApplication.class, args);
}
}Copy
Now the application is ready to test.

4. Testing

We can use the curl command to consume the secured application. First, let’s try to request
the /home without providing any security credentials:

curl --location --request GET 'http://localhost:8080/home'Copy


We get back the expected 401 Unauthorized. Now let’s request the same resource, but provide the API
Key and secret to access it as well:

curl --location --request GET 'http://localhost:8080/home' \


--header 'X-API-KEY: Baeldung'Copy
As a result, the response from the server is 200 OK.

5. Conclusion

In this article, we discussed the REST API security mechanisms. Then we implemented Spring Security
in our Spring Boot application to secure our REST API using the API Keys authentication mechanism.

As always, code samples can be found over on GitHub.

You might also like