Wednesday, 10 March 2021

Spring boot rest: Handle missing request parameter

Spring throws 'MissingServletRequestParameterException' when the request missing parameter.

 

How to handle this?

By overriding the method definition ‘handleMissingServletRequestParameter’, you can handle missing request parameter.

 

Example

@ControllerAdvice(basePackages = "com.sample.app")
public class CustomErrorHandler extends ResponseEntityExceptionHandler {

	@Override
	protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {
		String error = ex.getParameterName() + " -> parameter is missing in request";

		ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
		return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);

		// If you want to throw apiError directly, uncomment this
		// return new ResponseEntity<Object>(apiError, apiError.getStatus());
	}
}

 

Find the below working application.

 

Step 1: Create new maven project 'spring-rest-custom-error-handler'.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>spring-rest-custom-error-handler</artifactId>
	<version>1</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
	</parent>

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

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>


		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>


	</dependencies>
</project>

 

Step 3: Create application.yml file under src/main/resources folder.

 

application.yml

server:
  port: 1234

 

Step 4: Create a package 'com.sample.app.model' and define Employee, ApiError classes.

 

Employee.java

 

package com.sample.app.model;

import javax.validation.constraints.NotNull;

public class Employee {
	private int id;
	
	@NotNull(message = "Firstname can't be null")
	private String firstName;
	
	@NotNull(message = "LastName can't be null")
	private String lastName;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	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;
	}

}

ApiError.java

package com.sample.app.model;

import org.springframework.http.HttpStatus;

public class ApiError {
	private HttpStatus status;
	private String message;
	private Object errors;

	public ApiError(HttpStatus status, String message, Object errors) {
		super();
		this.status = status;
		this.message = message;
		this.errors = errors;
	}


	public HttpStatus getStatus() {
		return status;
	}

	public void setStatus(HttpStatus status) {
		this.status = status;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public Object getErrors() {
		return errors;
	}

	public void setErrors(Object errors) {
		this.errors = errors;
	}

}


Step 5: Create a package ‘com.sample.app.exception.handler’ and define CustomErrorHandler class.

 

CustomErrorHandler.java

package com.sample.app.exception.handler;

import java.util.ArrayList;
import java.util.List;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.sample.app.model.ApiError;

@ControllerAdvice(basePackages = "com.sample.app")
public class CustomErrorHandler extends ResponseEntityExceptionHandler {
	@Override
	protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {

		List<String> errors = new ArrayList<String>();

		for (FieldError error : ex.getBindingResult().getFieldErrors()) {
			errors.add(error.getField() + " -> " + error.getDefaultMessage());
		}

		for (ObjectError error : ex.getBindingResult().getGlobalErrors()) {
			errors.add(error.getObjectName() + " -> " + error.getDefaultMessage());
		}

		ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage(), errors);
		return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);

		// If you want to throw apiError directly, uncomment this
		// return new ResponseEntity(apiError, apiError.getStatus());
	}

	@Override
	protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {
		String error = ex.getParameterName() + " -> parameter is missing in request";

		ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error);
		return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request);

		// If you want to throw apiError directly, uncomment this
		// return new ResponseEntity<Object>(apiError, apiError.getStatus());
	}
}


Step 7: Create a package ‘com.sample.app.config’ and define SwaggerConfig class.

 

SwaggerConfig.java

package com.sample.app.config;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Component
@EnableAutoConfiguration
@EnableSwagger2
public class SwaggerConfig {

	@Bean
	public Docket userApi() {

		return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().paths(PathSelectors.any())
				.apis(RequestHandlerSelectors.basePackage("com.sample.app.controller")).build();
	}

	private ApiInfo apiInfo() {

		return new ApiInfoBuilder().title("Query builder").description("Query builder using spring specification")
				.version("2.0").build();
	}
}


Step 8: Create a package ‘com.sample.app.controller’ and define EmployeeController.

 

EmployeeController.java

package com.sample.app.controller;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.validation.Valid;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.sample.app.model.Employee;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("api/v1/employees")
@Api(tags = { "This section contains all Employee Speicifc Operations" })
public class EmployeeController {

	private static AtomicInteger counter = new AtomicInteger();

	private static Map<Integer, Employee> internalCache = new ConcurrentHashMap<>();

	@RequestMapping(method = RequestMethod.POST)
	@ApiOperation(value = "Create new employee", notes = "Create new employee")
	public ResponseEntity<Employee> create(@RequestBody @Valid Employee employee) {
		int newId = counter.incrementAndGet();
		employee.setId(newId);

		internalCache.put(newId, employee);

		return ResponseEntity.status(HttpStatus.CREATED).body(employee);
	}

	@RequestMapping(method = RequestMethod.GET)
	@ApiOperation(value = "Get employee by id", notes = "Get employee by  id")
	public ResponseEntity<Employee> create(@RequestParam(name = "empId", required=true) Integer empId) {
		Employee emp = internalCache.get(empId);
		
		if(emp == null) {
			return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
		}
		return ResponseEntity.status(HttpStatus.OK).body(emp);
	}
}


Step 9: Define App class in com.sample.app package.

 

App.java

package com.sample.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

	public static void main(String args[]) {
		SpringApplication.run(App.class, args);
	}

}


Total project structure looks like below.





Run App.java.

 

Execute following curl command in terminal.

 

curl -X GET "http://localhost:1234/api/v1/employees" -H "accept: */*"

 

You will get customized error message.

$ curl -X GET "http://localhost:1234/api/v1/employees" -H "accept: */*"
{"status":"BAD_REQUEST","message":"Required Integer parameter 'empId' is not present","errors":"empId -> parameter is missing in request"}


You can download complete application from this link.

https://github.com/harikrishna553/springboot/tree/master/rest/spring-rest-custom-error-handler







 

 

 

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment