REST Spring coding

Problem :

1. By default when you invoke any REST URI ,if it is success then it returns 200k

but we want to return specific HTTP STATUS based on the operation.

eg ; Creation --> should return 201 


 Solution :

@PostMapping("users/user")

public ResponseEntity<User> createUser(@RequestBody User user)

userDaoService.save(user)(;


                //This will add the location of the resource in the header


URI uri=ServletUriComponentsBuilder.fromCurrentContextPath()

.path("users/{id}")

.buildAndExpand(user.getId()).toUri(); 

return ResponseEntity.created(uri).build();

 o/p: 

    It does not return anything in the body, but it contains the location of the resource created in the header section and also notice 201 status.







2. Problem 


    When the resource does not found, by default,REST API returns 500 internal server Errors.





We want to send the specific HTTP ERROR AND STATUS.


solution :


    Resource not found is not the server Error, so we want to return a specific user not found error.


    in the service layer ..handle the Error.


public User findById(int id){

return users.stream()

.filter(user->user.getId()==id))

.findFirst()

//.orElse(DEFAULT_USER)

.orElseThrow(()->new UserNotFoundException("user does not exist for the id "+id);


}


UserNOtFoundException is a user-defined Runtime exception 


import org.springframework.http.HttpStatus;

import org.springframework.web.bind.annotation.ResponseStatus;


@ResponseStatus(code = HttpStatus.NOT_FOUND)

public class UserNotFoundException extends RuntimeException {


public UserNotFoundException(String message){

super(message);

}}



}

}






Problem : 


3. Response status retuned is the default implementation from the Spring boot REST, it will return different parameters based on the error type.


Generally, we need to be consistent in the exception message payload. It would not be good if each of the services returns different exception payload parameters.


Solution :


 Define a standard exception structure that would follow across all the services.


NOTE: This is not required if the default exception message provided by spring is sufficient for you.










package com.example.exceptions;


import java.time.LocalDate;


public class MyExceptionResponse {


private LocalDate timeStamp;

private String message;

private String details;

public MyExceptionResponse(LocalDate timeStamp, String message, String details) {

super();

this.timeStamp = timeStamp;

this.message = message;

this.details = details;

}



//getters and setters

}




===============================================


Customized Exception class: 




package com.example.exceptions;


import java.time.LocalDate;



import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.context.request.WebRequest;

import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;



@ControllerAdvice

public class MyCustomizedExceptionHandller extends ResponseEntityExceptionHandler{

@ExceptionHandler(Exception.class)

protected ResponseEntity<Object> handleAllException(Exception ex, WebRequest request) {

MyExceptionResponse exResponse=new MyExceptionResponse(LocalDate.now(), ex.getMessage(), request.getDescription(true));

return new ResponseEntity<Object>(exResponse,HttpStatus.INTERNAL_SERVER_ERROR);

}

@ExceptionHandler(UserNotFoundException.class)

protected ResponseEntity<Object> handleUserNotFoundException(Exception ex, WebRequest request) {

MyExceptionResponse exResponse=new MyExceptionResponse(LocalDate.now(), ex.getMessage(), request.getDescription(true));

return new ResponseEntity<Object>(exResponse,HttpStatus.NOT_FOUND);

}


}



}



===========================================================



Problem : 


Using javax validation API 


Solution :  


import javax.validation.constraints.Size;


public class User {

private Long id;

@Size(min = 3,message = "username should be more than 2 characters")

private String userName;

public User(Long id, String userName) {

this.id=id;

this.userName=userName;

}


//getters and setters


}



In your customized exception class overide this method.


@Override

protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,HttpHeaders headers, HttpStatus status, WebRequest request) {

//This will give error message which is mention at resource level which is binded while using  @Size 

//MyExceptionResponse exResponse=new MyExceptionResponse(LocalDate.now(),ex.getBindingResult().toString(), request.getDescription(false));

MyExceptionResponse exResponse=new MyExceptionResponse(LocalDate.now(),"User name must be atlease 3 characters", request.getDescription(false));

return new ResponseEntity<Object>(exResponse,HttpStatus.BAD_REQUEST);

}



result :




=========================================

Problem :


 How to use HATEOAS :


import org.springframework.hateoas.EntityModel;

import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;




@GetMapping("/users/{id}")

public EntityModel<User> findUserById(@PathVariable int id) {

User user=userDaoService.findById(id);


//implementing HATEOAS

// Requirment : Response should also contain the link to retrieve all users.

EntityModel<User> entityModel =EntityModel.of(user);

WebMvcLinkBuilder linkTo = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass())

  .findAllUsers()

);

 

entityModel.add(linkTo.withRel("all-users"));

return entityModel;

}




o/p : 




Problem :


 How to use Internalization in REST :







Step 1: 


             @Bean

public LocaleResolver localeResolver() {

SessionLocaleResolver localeResolver=new SessionLocaleResolver();

localeResolver.setDefaultLocale(Locale.US);

return localeResolver;

}



@Bean

public  ResourceBundleMessageSource messageSource() {

ResourceBundleMessageSource messageSource=new ResourceBundleMessageSource();

messageSource.setBasename("messages");

return messageSource;

}


Marked with greeen part can be repalced with the property in applications.properties file.


 spring.messages.basename=messages






Step 2:



@RestController

public class HelloWorldController {


@Autowired

private MessageSource messageSource;

@GetMapping("/i18")

public String helloWorldInternalization(@RequestHeader(name = "Accept-language",required = false) Locale locale) {

return messageSource.getMessage("good.morning.message",null, locale);

}

}






Step 3 :



Create property files as with below names under src/resources folder :



messages.properties


good.morning.message=Good Morning



message_fr.properties



good.morning.message=Bonjur






Approach II : We don't need to use @RequestHeader in each controller

  
     @GetMapping("/i18_test")

public String helloWorldInternalization1() {

return messageSource.getMessage("good.morning.message",null, LocaleContextHolder.getLocale());

}



@Bean

public LocaleResolver localeResolver() {


AcceptHeaderLocaleResolver localeResolver=new AcceptHeaderLocaleResolver();

localeResolver.setDefaultLocale(Locale.US);

return localeResolver;

}



application.properties 


spring.messages.basename=messages





Content Negotiation : -Support for XML


Problem :

  How to support XML in REST service 


Solution :

Just add the below dependencies and restart the app


<dependency>

    <groupId>com.fasterxml.jackson.dataformat</groupId>

    <artifactId>jackson-dataformat-xml</artifactId>

</dependency>









Problem :

How to use Swagger 

Swagger - Documention tool for your REST API. Its like WSDL in SOA websevices.



solution :




<dependency>

<groupId>io.springfox</groupId>

<artifactId>springfox-boot-starter</artifactId>

<version>3.0.0</version>

</dependency>


<dependency>

<groupId>io.springfox</groupId>

<artifactId>springfox-swagger-ui</artifactId>

<version>3.0.0</version>

</dependency>


<dependency>

<groupId>io.springfox</groupId>

<artifactId>springfox-swagger2</artifactId>

<version>3.0.0</version>

</dependency>



in application.properties file :

spring.mvc.pathmatch.matching-strategy = ANT_PATH_MATCHER





import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;


import springfox.documentation.spi.DocumentationType;

import springfox.documentation.spring.web.plugins.Docket;

import springfox.documentation.swagger2.annotations.EnableSwagger2;


@Configuration

@EnableSwagger2

public class SwaggerConfig {

@Bean

public Docket api() {

return new Docket(DocumentationType.SWAGGER_2);

}


}




Restart the application and access below uris


http://localhost:8080/swagger-ui/index.html#/

http://localhost:8080/v2/api-docs



Problem : 


Monitor spring REST webservices :


solution: Use actuators



Problem:  

 How to filter specific fields in a REST response. or if you do not want to include some fields as part of the rest response ?


Solution:  Static Filtering 


   1)@JsonIgnore --> on field level


   @JsonIgnore

    private String field2;


2)@JsonIgnoreProperties --class level



     @JsonIgnoreProperties({"field2","field3"}

    public class SomeBean {


private String field1;

@JsonIgnore

private String field2;

private String field3;

private String field4;


//gettconsturctor

//getters and setters

}


Below Response ignored the field2 and filed3





Dynamic filtering :


Filter fields dynamically



    

@JsonFilter("somebeanFilter")

public class SomeBean {


private String field1;


private String field2;

private String field3;

private String field4;

public SomeBean(String field1, String field2, String field3, String field4) {

super();

this.field1 = field1;

this.field2 = field2;

this.field3 = field3;

this.field4 = field4;

}

//getters and setters

}





@GetMapping(produces ="application/json",value="/somebean")

//here want to filter out only field1 and field2 (Not to be included in response)

public MappingJacksonValue some() {

SomeBean somebean=new SomeBean("value1","value2","value3","value4");

SimpleBeanPropertyFilter sbpf=SimpleBeanPropertyFilter.filterOutAllExcept("field3","field4");

FilterProvider filterProvider=new SimpleFilterProvider().addFilter("somebeanFilter", sbpf);

MappingJacksonValue mappingFilter=new MappingJacksonValue(somebean);

mappingFilter.setFilters(filterProvider);

return mappingFilter;

}

@GetMapping("/listbean")

public List<SomeBean> someBeanList(){

return Stream.of(new SomeBean("value1","value2","value3","value4"),

new SomeBean("value1","value2","value3","value4"))

.collect(Collectors.toList());

}








REST API Version



















Comments

Popular posts from this blog

Richardson maturity model