Spring Boot – REST with Custom Error Msg and Log | Code Factory


Donate : Link

Medium Blog : Link

Applications : Link

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.10.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.codeFactory</groupId>
	<artifactId>spring-boot-custom-err-response-and-logger</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-custom-err-response-and-logger</name>
	<description>Rest endpoint with validation</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

SpringBootCustomErrResponseAndLogger.java

package com.codeFactory;

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

/**
 * @author code.factory
 */
@SpringBootApplication
public class SpringBootCustomErrResponseAndLogger {

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

}

WebMvcConfig.java

package com.codeFactory;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.codeFactory.utility.LogInterceptor;

/**
 * @author code.factory
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

	@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor());
    }
}

EmployeeController.java

package com.codeFactory.controller;

import javax.validation.Valid;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.codeFactory.model.Employee;

/**
 * @author code.factory
 */
@RestController
@Validated
public class EmployeeController {

	@PostMapping(value = "employee", consumes = "application/json", produces = "application/json")
	public ResponseEntity<Employee> displayEmployee(@Valid @RequestBody Employee employee) {
		return ResponseEntity.ok(employee);
	}

}

Employee.java

package com.codeFactory.model;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

/**
 * @author code.factory
 */
public class Employee {

	@Min(value = 1, message = "Id can't be less than 1 or bigger than 999999")
	@Max(999999)
	private int id;

	@Size(max = 15, message = "Name must be less than 15 characters")
	@NotBlank
	@Pattern(regexp = "^[A-Za-z ]*$", message = "Only characters allowed")
	private String name;

	@NotBlank
	@Pattern(regexp = "^[A-Za-z ]*$", message = "Only characters allowed")
	private String designation;

	@NotBlank
	private String address;

	public int getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDesignation() {
		return designation;
	}

	public void setDesignation(String designation) {
		this.designation = designation;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

}

LogInterceptor.java

package com.codeFactory.utility;

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * @author code.factory
 */
public class LogInterceptor extends HandlerInterceptorAdapter {

	private static final Logger LOGGER = LoggerFactory.getLogger(LogInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		String requestId = UUID.randomUUID().toString();
		log(request, response, requestId);
		long startTime = System.currentTimeMillis();
		request.setAttribute("startTime", startTime);
		request.setAttribute("requestId", requestId);
		return true;
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		super.afterCompletion(request, response, handler, ex);
		long startTime = (Long) request.getAttribute("startTime");
		long endTime = System.currentTimeMillis();
		long executeTime = endTime - startTime;
		LOGGER.info("Request Id: {}, Handle: {} , Request take time: {}", 
				request.getAttribute("requestId"), handler, executeTime);
		LOGGER.info("Response status: {}, Content type: {}", 
				response.getStatus(), response.getContentType());
	}

	private void log(HttpServletRequest request, HttpServletResponse response, String requestId) {
		LOGGER.info("Request Id: {}, Host {}  HttpMethod: {}, URI: {}", 
				requestId, request.getHeader("host"), request.getMethod(), request.getRequestURI());
	}
}

ValidationUtil.java

package com.codeFactory.utility;

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

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;

/**
 * @author code.factory
 */
@ControllerAdvice
public class ValidationUtil {

	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(code = HttpStatus.BAD_REQUEST)
	@ResponseBody
	public Error handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, WebRequest request) {
		BindingResult result = ex.getBindingResult();

		List<String> errorList = new ArrayList<>();
		result.getFieldErrors().forEach((fieldError) -> {
			errorList.add(fieldError.getObjectName() + "." + fieldError.getField() + " : "
					+ fieldError.getDefaultMessage() + " : rejected value [" + fieldError.getRejectedValue() + "]");
		});
		result.getGlobalErrors().forEach((fieldError) -> {
			errorList.add(fieldError.getObjectName() + " : " + fieldError.getDefaultMessage());
		});

		return new Error(HttpStatus.BAD_REQUEST, errorList);
	}

	public static class Error {
		private int errorCode;
		private String error;
		private List<String> fieldErrors = new ArrayList<>();

		public Error(HttpStatus status, List<String> fieldErrors) {
			this.errorCode = status.value();
			this.error = status.name();
			this.fieldErrors = fieldErrors;
		}

		public int getErrorCode() {
			return errorCode;
		}

		public void setErrorCode(int errorCode) {
			this.errorCode = errorCode;
		}

		public String getError() {
			return error;
		}

		public void setError(String error) {
			this.error = error;
		}

		public List<String> getFieldErrors() {
			return fieldErrors;
		}

		public void setFieldErrors(List<String> fieldErrors) {
			this.fieldErrors = fieldErrors;
		}
	}
}
com.codeFactory.utility.LogInterceptor   : Request Id: 1ae345fb-7d48-4f94-b507-d4e18dd29d36, Host localhost:8080  HttpMethod: POST, URI: /employee
com.codeFactory.utility.LogInterceptor   : Request Id: 1ae345fb-7d48-4f94-b507-d4e18dd29d36, Handle: com.codeFactory.controller.EmployeeController#displayEmployee(Employee) , Request take time: 736
com.codeFactory.utility.LogInterceptor   : Response status: 200, Content type: application/json
com.codeFactory.utility.LogInterceptor   : Request Id: 210e19d3-11d5-415d-9a3b-e89f85c63667, Host localhost:8080  HttpMethod: POST, URI: /employee
com.codeFactory.utility.LogInterceptor   : Request Id: 210e19d3-11d5-415d-9a3b-e89f85c63667, Handle: com.codeFactory.controller.EmployeeController#displayEmployee(Employee) , Request take time: 114
com.codeFactory.utility.LogInterceptor   : Response status: 400, Content type: application/json

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s