How to get request's URI from WebRequest in Spring?
Solution 1
Found the solution. Casting WebRequest
to ServletWebRequest
solved the purpose.
((ServletWebRequest)request).getRequest().getRequestURI().toString()
returns the complete path - http://localhost:8080/signup
Solution 2
There are multiple solutions to this problem.
1) One can get request URI and client information from WebRequest using webRequest.getDescription(true).
true will show user's information such as client id and false will just print URI.
2) Instead of WebRequest of Use HttpServletRequest directly in method definition as
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
HttpHeaders headers, HttpStatus status, WebRequest request, HttpServletRequest httpRequest) {
logger.info(httpRequest.getRequestURI());
return handleExceptionInternal(ex, errorMessage(HttpStatus.BAD_REQUEST, ex, request), headers, HttpStatus.BAD_REQUEST, request);
}
Solution 3
ResponseEntityExceptionHandler explains A convenient base class for @ControllerAdvice classes that wish to provide centralized exception handling across all @RequestMapping methods through @ExceptionHandler methods. here
In Spring Boot 2.1.6, You can write as below:
RestExceptionHandler.java
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(RestExceptionHandler.class);
@ExceptionHandler(ResourceNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFound(ResourceNotFoundException ex, final HttpServletRequest httpServletRequest) {
ApiError apiError = new ApiError(HttpStatus.NOT_FOUND);
apiError.setMessage("Resource not found");
apiError.setDebugMessage(ex.getMessage());
apiError.setPath(httpServletRequest.getRequestURI());
return buildResponseEntity(apiError);
}
private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, apiError.getStatus());
}
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED);
apiError.setMessage(ex.getMessage());
apiError.setPath(((ServletWebRequest)request).getRequest().getRequestURI().toString());
logger.warn(ex.getMessage());
return buildResponseEntity(apiError);
}
}
Let's start by implementing a simple structure for sending errors:
ApiError.java
public class ApiError {
// 4xx and 5xx
private HttpStatus status;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private LocalDateTime timestamp;
// holds a user-friendly message about the error.
private String message;
// holds a system message describing the error in more detail.
@JsonInclude(value = Include.NON_EMPTY)
private String debugMessage;
// returns the part of this request's URL
private String path;
@JsonInclude(value = Include.NON_EMPTY)
private List<String> details=new ArrayList<>();
// setters & getters
}
ResourceNotFoundException.java
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(String msg) {
super(msg);
}
Solution 4
Access the attribute of WebRequest object:
Object obj = webRequest.getAttribute("org.springframework.web.util.UrlPathHelper.PATH", 0)
String uri = String.valueOf(obj);
webRequest.getAttribute(String attributeName, int scope);
// scope can be either:
// 0: request
// 1: session
// valid attribute names can be fetched with call:
String[] attributeNames = webRequest.getAttributeNames(0); //scope is request
Valid attribute names are:
org.springframework.web.util.UrlPathHelper.PATH
org.springframework.web.context.request.async.WebAsyncManager.WEB_ASYNC_MANAGER
org.springframework.web.servlet.HandlerMapping.bestMatchingHandler
org.springframework.web.servlet.DispatcherServlet.CONTEXT
org.springframework.web.servlet.resource.ResourceUrlProvider
characterEncodingFilter.FILTERED
org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR
org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE
org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER
formContentFilter.FILTERED
org.springframework.web.servlet.HandlerMapping.bestMatchingPattern
requestContextFilter.FILTERED
org.springframework.web.servlet.DispatcherServlet.OUTPUT_FLASH_MAP
org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping
org.springframework.web.servlet.DispatcherServlet.FLASH_MAP_MANAGER
org.springframework.web.servlet.HandlerMapping.uriTemplateVariables
org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER
org.springframework.core.convert.ConversionService
The Coder
A self-taught developer interested in all technical stuffs.
Updated on July 12, 2022Comments
-
The Coder almost 2 years
I am handling REST exceptions using
@ControllerAdvice
andResponseEntityExceptionHandler
in a spring Rest webservice. So far everything was working fine until I decided to add theURI
path(for which exception has occurred) into the BAD_REQUEST response.@ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { logger.info(request.toString()); return handleExceptionInternal(ex, errorMessage(HttpStatus.BAD_REQUEST, ex, request), headers, HttpStatus.BAD_REQUEST, request); } private ApiError errorMessage(HttpStatus httpStatus, Exception ex, WebRequest request) { final String message = ex.getMessage() == null ? ex.getClass().getName() : ex.getMessage(); final String developerMessage = ex.getCause() == null ? ex.toString() : ex.getCause().getMessage(); return new ApiError(httpStatus.value(), message, developerMessage, System.currentTimeMillis(), request.getDescription(false)); }
ApiError is just a Pojo class:
public class ApiError { private Long timeStamp; private int status; private String message; private String developerMessage; private String path; }
But WebRequest has not given any api to get the path for which the request failed. I tried:
request.toString()
returns -> ServletWebRequest: uri=/signup;client=0:0:0:0:0:0:0:1
request.getDescription(false)
returns -> uri=/signup
getDescription
is pretty close to the requirement, but doesn't meet it. Is there any way to get only the uri part?