Restful webservices 101: Things to remember while developing Restful webservices.

Swagger,Content negotiation, Exception handing, Filters- dynamic and static,Versioning, Hateos, Customizing REST API response, Actuator, HAL Explorer, Spring-security and Command-line runner

Nikitha Gullapalli
7 min readApr 23, 2023

While learning how to develop REST APIs, can across some misc good to know topics. Want to keep all of them in one place for reference. This is a high level document on what each of these topics mean and how they work. To see the implementation refer to the github project.

GitHub:https://github.com/nikitha2/SocialMedia_BackendWithSpringBoot.git

LinkedIn: https://www.linkedin.com/in/nikitha-gullapalli/

1. Content negotiation -> expect response as json or xml. Default is Json, but can add dependency in pom to support xml

2. Swagger- Add dependency in pom.xml

<! - path: http://localhost:8080/swagger-ui/index.html#/ →
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>

enter this url http://localhost:8080/swagger-ui/index.html in console and you should see swagger documentation.

3. Exception handing- override any exception you want and customise the message being sent as response with a bean.

E.g: https://github.com/nikitha2/SocialMedia_BackendWithSpringBoot.git

4. Internationalization — Also called i18n. When I have users in differnet countries. I want my apis to support different languages. Simply add header in api request.

Accept-Language = en or fr or nl ..

Accept-Language indicates local language customer prefers

a. To achieve this create messages.properties in src/main/resources. Same positions as application.properties.

b.In your project add file messages.properties. And for each language you want to support create file -> messages_<language>.properties

E.g: messages_nl.properties

Add string -> good.morning.message = "Goedemoergen i18n"

c. In the controller or where ever you want to read it inject MessageSource via a constructor or Autowiring and get resource as shown below.

Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage("good.morning.message", null,"Default message", locale );

E.g: https://github.com/nikitha2/SocialMedia_BackendWithSpringBoot.git

5. Versioning — If my app has v1 and now I want o introduce a breaking change. I introduce v2. This way older versions of the app wont be broken.

How to achieve this? We can achieve this in 4 differnt ways.

a. URL : v1/users and v2/users @GetMapping the controller class.

// http://localhost:8080/v1/person
@GetMapping("/v1/person")
public PersonV1 getPersonV1() {
return new PersonV1(" Goerge Kristansa");
}

// http://localhost:8080/v2/person
@GetMapping("/v2/person")
public PersonV2 getPersonV2() {
return new PersonV2("Goerge", "Kristansa");
}

b. Request Parameter : {version}/users as path for @GetMapping and accept {version} as a request parameter

// http://localhost:8080/person?version=1
@GetMapping(path = "/person", params = "version=1")
public Object getPersonRequestParamV1() {
return new PersonV1(" Goerge Kristansa");
}

// http://localhost:8080/person?version=2
@GetMapping(path = "/person", params = "version=2")
public Object getPersonRequestParamV2() {
return new PersonV2("Goerge", "Kristansa");
}

c. Header : Send it as a header and make call as shown below

// http://localhost:8080/person and add header X-API-VERSION=1
@GetMapping(path = "/person", headers = "X-API-VERSION=1")
public Object getPersonWithHeaderV1() {
return new PersonV1(" Goerge Kristansa h");
}

// http://localhost:8080/person and add header X-API-VERSION=2
@GetMapping(path = "/person", headers = "X-API-VERSION=2")
public Object getPersonWithHeaderV2() {
return new PersonV2("Goerge h", "Kristansa h");
}

d. Media Type : Send it as a header and make call as shown below

// http://localhost:8080/person and add header Accept = application/getPersonForV1+json
@GetMapping(path = "/person", produces = "application/getPersonForV1+json")
public Object getPersonWithAcceptV1() {
return new PersonV1(" Goerge Kristansa A");
}

// http://localhost:8080 add header Accept = application/getPersonForV2+json
@GetMapping(path = "/person", produces = "application/getPersonForV2+json")
public Object getPersonWithAcceptV2() {
return new PersonV2("Goerge A", "Kristansa A");
}

So which approach is preffered or industry standard? There is no perfect solution. Each solution has its own advantages and disadvatages. URI or request parameter being my preffered choice.

6.Hateos: Hyper media as the engine of Application State

Say I want to add text on click execute another api call. I want a response with links and below response for https://localhost/users/{id}. That's method getUser() in our project.

{
"id": 1,
"userBody":{
"name": "Red",
"birthDate": "2003-04-20",
"links":{
"all-users": {
"href": "http://localhost:8080/users"
}
}
}
}

Can achieve this with 2 types of implementation:

a. Create custom bean. Text and href. On click open that link. This will be difficult to maintain.

b. Can do this using Hateos. This is an industry standard so all applications can follow this.

Hateos allow this without changing the bean. Will achieve this will user.

b.1. First add Hateos dependency in pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

b.2. EntityModel : Because I want to return https://localhost/users/{id} with Hateos. Wrap response in EntityModel - EntityModel<User> entityModel = EntityModel.of(user)

WebMvcLinkBuilder: Used to link the link to another api that needs to get executed when text is clicked.

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;

// GET /users/{id} -> returns back a user {id}
@GetMapping("/users/{id}")
public EntityModel<User> getUser(@PathVariable Integer id) {
User user = userDaoService.getUser(id);

if (user == null) {
throw new UserNotFoundException("id: " + id);
}
// wraps user with entityModel
EntityModel<User> entityModel = EntityModel.of(user);

/** Create a link with controller. As getAllUsers() is in the same controller.
can initiallize controller with this.getClass()
linkTo(<controller>).<methodInThatController> to be called on click on link.
*/
WebMvcLinkBuilder link = linkTo(methodOn(this.getClass()).getAllUsers());

// links the link with reference to string
entityModel.add(link.withRel("all-users"));
return entityModel;
}

7. Customizing REST API response

a. Serialization: Converting Object to steam. Most popular is Jackson

a.1. Customize field name in response- @JSONProperty

 @JsonProperty("user_name")
private String name;

@JsonProperty("birth_date")
private LocalDate birthDate;

Now response will have “user_name” instead of “name”

a.2. Filtering — Filter filelds in the response and send back selective fields only in response. Two types of filtering is static and dynamic filtering

a.2.1. Static Filtering:

@JsonIgnore — Add it on top of the fiels you want to ignore in response. OR

@JsonIgnoreProperties({“field3”,”field2"}) — At the top of class and a list of field to ignore in response

a.2.2. Dynamic filtering: @JsonFilter with FilterProvider . Add a dynamic filter as shown below.

8. Actuator : Help maintain prod environamet. Gives health and other information about the application. By default only health details will be present.

To see more information add the below command to application.properties file.

management.endpoints.web.exposure.include=*

Add dependency in pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

9. HAL Explorer: HAL stands for JSON hyperlink application language. It is a simple format that gives a consistence and simple way to hyperlink between resources in the application.

Advantage is it allows non-technical user to play with APIs. Spring boot HAL explore auto-configues this for us. Simple add the started project in pom.xml.

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-explorer</artifactId>
</dependency>

Now goto browser and type http://localhost:8080/ you’ll see HAL explorer

Type /users. This should give more infomation on /users api.

10. Spring security:

Github: https://github.com/nikitha2/SocialMedia_BackendWithSpringBoot.git

  1. Right now anyone can add/read users and posts from out db,but this is not good. To avoid this we can add security and a password.
  2. Add sprint-security dependency and run application. This should create password. You will see it in the console.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

3. Now if you try accessing db it will ask for credentials. Enter credentials username and password as shown below in application.properties file.

#------------Spring-security------------
spring.security.user.name=username
spring.security.user.password=mysqlpassword

Now start the service and goto a browser to make the rest call. You will see a login page before the response shows up. After entering username and password initialized in applications.properties. Endpoint response will show up.

Also, When you run a POST sql you will get a forbidden error. The reason is Spring-security runs a series of filter chain commands. Some of them are

10.1) All requests should be authenticated  -> This makes sure all requests are authenticated
10.2) If a request is not authenticated, a web page is shown -> if not authenticated will show a webpage for username and password. But for rest services we want to show a popup instead of webpage
10.3) CSRF -> POST, PUT are blocked because of this. So we need to diable it.

Login page is good for a website, but for an endpoint it is customary to show pop-up for login as shown below. How do we achieve that? We override the filter chains of spring-security.

However in spring when you are overriding a certain filter chain we need to re-write a all of them. For this reason added class SpringSecurityConfiguration to define spring-security configurations.(File in github project: src/main/java/com/springboot/social_media/security)

Now login is displayed as a popup and POST calls are successful.

11. Command Line runner:

Say you created a rest service with a database. How you want to run a few queries at the start of the service. We can achieve this with a commandLineRunner file that runs at the start of the service. You can add a class that implements CommandLineRunner and say what needs to happen.

Github: https://github.com/nikitha2/StartProject_JDBC_JPA_hibernate.git

Path:src/main/java/com/springboot101/learnh2jpaandhibernate/courses/CourseCommandLineRunner.java

References:

https://github.com/in28minutes/spring-microservices-v2/blob/main/02.restful-web-services/01-step-by-step-changes/v2.md#step-21

--

--