How to know the handler spring controller class and the handler method for the specified HttpServletRequest object - spring

I need to somehow access the handler method with the reflection in the web filter and get the requestPattern value without passing the control to the dispatcher servlet. How can I do this?
I only have for that HttpServletRequest object and somehow I can also #Autowire there ApplicationContext object.
Thanks.

I have started to look how the DispatcherServlet itself decides which handler method it will give the control and implemented with such a way.
Here is the code:
//Initialization in filter constructor
....
final HandlerMapping handlerMappings = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, HandlerMapping.class, true, false).get("requestMappingHandlerMapping");
....
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
.....
Method mappingMethod = null;
try {
mappingMethod = ((HandlerMethod)handlerMappings.getHandler(request).getHandler()).getMethod();
RequestMapping requestMapping = mappingMethod.getAnnotation(RequestMapping.class);
final String requestPattern = requestMapping.value();
}
catch(Exception ex){
logger.error("Error getting the mapping bean for the request URL " + request.getRequestURI(), ex);
return;
}
....
}

On top of the proposal of #Arsen I would suggest to do like this:
try {
handlerMappings.getHandler(request);
String requestPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
} catch (Exception ex) {
logger.error("Error getting the mapping bean for the request URL " + request.getRequestURI(), ex);
return;
}
The getHandler() instruction is enoght to trig the spring lookup of the correct controller that will also set the HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE attribute, this is usually available in controllers but not in filters.
Be aware that these solutions will waste some resources, cause spring will do the lookup again after that.

Related

How can I make sure exceptions during parsing lead to the same kind of response as the (custom) response returned for validation failures?

I'm using Spring to create an API, but I'm having some trouble introducing custom error reporting on (a part of) the validation of the request body.
When parsing/validation errors occur, I want to give a custom response back to the user.
This works well for fields annotated with #Valid along with validators like #javax.validation.constraints.NotNull by using a custom ResponseEntityExceptionHandler annotated with #ControllerAdvice.
It does not work however if an Exception is thrown while parsing the request body (before the validations even run). In that case I get an html error page with status 500 (Server Error)
How can I make sure the exceptions during parsing lead to the same kind of response as the (custom) one I return for validation failures?
My endpoint's code looks like this:
#RequestMapping(value= "/endpoint"
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
default ResponseEntity<Object> postSomething(#Valid #RequestBody MyRequestBody requestData){
// ...
}
MyRequestBody class looks like this:
#Validated
public class MyRequestData {
#JsonProperty("stringValue")
private String stringValue = null;
#NotNull
#Valid
public String getStringValue() {
return stringValue;
}
// ...
public enum EnumValueEnum {
VALUE_1("value 1"),
VALUE_1("value 2");
private String value;
EnumValueEnum(String value) {
this.value = value;
}
#Override
#JsonValue
public String toString() {
return String.valueOf(value);
}
#JsonCreator
public static EnumValueEnum fromValue(String text) {
if(text == null){
return null;
}
for (EnumValueEnum b : EnumValueEnum.values()){
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
throw new HttpMessageNotReadableException("EnumValueEnum \"" + text + "\" does not exist");
}
}
#JsonProperty("enumValue")
private EnumValueEnum enumValue = null;
}
The custom validation error handling (and reporting) looks like this:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class MyValidationHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse(ex.getBindingResult().getFieldErrors()));
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) ex.getCause()));
}
}
In this code, if a user sends a request with an enum value that doesn't exist, an HttpMessageNotReadableException is thrown. I would like to catch that somewhere and replace it with a custom response that is consistent with the other exception handling I do. Where/How can I do that?
I found a solution to my own problem.
You can actually use Spring MVC's normal exception handling:
Annotating a method with #ExceptionHandler will make Spring try to use it for exception handling for the exception type specified (in the annotation's value field or the method's argument). This method can be placed in the controller or even in the ResponseEntityExceptionHandler I use for the other validation response handling.
#ExceptionHandler
public ResponseEntity handle(HttpMessageConversionException e){
// return status(BAD_REQUEST).body(new ValidationResponse((JsonMappingException) e.getCause()));
}
Mind which type of exception you handle:
The catch here was that the exception thrown while parsing is wrapped in (some subtype of) a JsonMappingException which in turn is wrapped again in a HttpMessageConversionException.
e instanceof HttpMessageConversionException
e.getCause() instanceof JsonMappingException
e.getCause().getCause() // == your original exception
The #ExceptionHandler should therefor accept HttpMessageConversionException instead of the originally thrown exception (which in my case was HttpMessageNotReadableException)
It will not work if you write an #ExceptionHandler that only accepts your original Exception!

Struts 2: Impossible get session after response is commited

I've already read the another article about it but it doesn't help me to find a generic solution for the entire application.
I've kept a big legacy web application (Struts2, Spring Boot, and Tomcat embedded) and I'm facing these following error.
2018-08-14 11:01:11.872 [http-nio-10010-exec-114] ERROR o.a.c.c.C.[.[localhost].[/].[jsp] - Servlet.service() for servlet jsp threw exception java.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2953)
at org.apache.catalina.connector.Request.getSession(Request.java:2367)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:592)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:537)
at org.apache.jasper.runtime.PageContextImpl.initialize(PageContextImpl.java:137)
at org.apache.jasper.runtime.JspFactoryImpl.internalGetPageContext(JspFactoryImpl.java:109)
at org.apache.jasper.runtime.JspFactoryImpl.getPageContext(JspFactoryImpl.java:60)
at org.apache.jsp.jsp.errorPage_jsp._jspService(errorPage_jsp.java:127)
Other example:
2018-08-14 11:01:11.870 [http-nio-10010-exec-114] ERROR o.a.c.c.C.[.[.[.[.q.q.c.RedirectResourceServlet] - Servlet.service() for servlet [com.mydomain.myapplication.control.RedirectResourceServlet] in context with path [] threw exceptionjava.lang.IllegalStateException: Cannot create a session after the response has been committed
at org.apache.catalina.connector.Request.doGetSession(Request.java:2953)
at org.apache.catalina.connector.Request.getSession(Request.java:2367)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:908)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:240)
It occurs in different places like *Actions.java and JSP files.
Does someone know how and which is the best approach to intercept all request.getSession() to check if it was already committed? And how could I in a Struts 2 context create a new session? The request.getSession(true) doesn't work when the response.isCommitted() is true.
I've already tried to create a #WebFilter where I check and try to create a new session, but it doesn't work as expected. I also believe it is not the best approach.
#WebFilter(urlPatterns = "/*")
public class SessionValidatorFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(getClass().getName());
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (servletResponse.isCommitted() && servletRequest instanceof HttpServletRequest && servletResponse instanceof HttpServletResponse) {
logger.debug("Response was already committed. Trying to create a new session.");
try {
HttpSession session = ((HttpServletRequest) servletRequest).getSession(true);
if (session != null)
logger.debug("Session created.");
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception e) {
logger.debug("Error trying to create new Session.");
}
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
}

Spring mvc - Configuring Error handling for XML and JSON Response

i have one REST API method :which will return Xml as response . Just for simplicity assume it throws simple Exception.
#RequestMapping(value = "machine/xmlData", method = RequestMethod.GET, produces = "application/xml")
public ResponseEntity<String> getXml(HttpServletRequest request)
throws Exception {
return getDataFromService();
}
Now i am handling the Exception in REST Controller like this.
This is generic Exception Handle method, for other API methods as well.(Xml or JSON Response)
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity HandleException(Exception ex, HttpServletRequest request) {
ex.printStackTrace();
// here logic to generate Custom error Object
return new ResponseEntity<Object>(customErrorObject, HttpStatus.INTERNAL_SERVER_ERROR);
}
Case 1: Accept :"application/xml" and valid Response from Service
Everything works fine.
Case 2: Accept :"application/xml" and Exception from Service
then i get 406 Not Representable
As per my understanding it is
because ResponseEntity from HandleException is JSON and accept header
is "application/xml" thats why i am getting 406.
Is there anyway that i can send the error Response from HandleException method as xml and json ?
I know on REST API methods we can define something like this produces={"application/json","application/xml"} i am struggling to put this on HandleException Method.
Any tip would be of great help.
Thanks.
You could take advantage of the spring-mvc HttpMessageConverters by using the #ResponseBody annotation( https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc). This annotation is responsible for choosing the correct messageConverter for a given response type.
For your response to be xml or json compatible you need to do the following:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class WrappedExceptionResponse {
public String respone;
public String getRespone() {
return respone;
}
public void setRespone(String respone) {
this.respone = respone;
}
}
And change your exception handler method to
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public #ResponseBody WrappedExceptionResponse HandleException(Exception ex, HttpServletRequest request) {
// ex.printStackTrace();
// here logic to generate Custom error Object
WrappedExceptionResponse resp=new WrappedExceptionResponse();
resp.setRespone(ex.getMessage());
return resp;
And then your exception response would be dependent on the content-type you give.

Spring Exception Handler log all exceptions but return original response

So I am trying to log all uncaught exceptions returned by the controllers of a spring project in a generic fashion.
I was able to do this with the following exception handler:
#ControllerAdvice
public class ControllerConfig {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleBadRequest(HttpMessageNotReadableException e) {
logger.warn("Returning HTTP 400 Bad Request", e);
throw e;
}
#ExceptionHandler(AccessDeniedException.class)
public void defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
logger.error("Error in request:" + request.getRequestURL(), e);
throw e;
}
This also returns the error responses of the request, so I don't have to differentiate between all the different error response codes.
However, for every invocation of the method a second error log is created because of the exception thrown in the method:
Code is from org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking #ExceptionHandler method: " + exceptionHandlerMethod);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
}
catch (Exception invocationEx) {
if (logger.isErrorEnabled()) {
logger.error("Failed to invoke #ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
return null;
}
So is there a smarter way to return the original exception of the method?
It depends on what do you mean by "a smarter way to return the original exception". What exactly would you like to return to the client? If this is just the message of the exception you can simply return it from the exception handler and annotate the method with #ResponseBody. Spring will do the rest for you.
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public String handleBadRequest(HttpMessageNotReadableException e) {
logger.warn("Returning HTTP 400 Bad Request", e);
throw e.getMessage();
}
You can also return some custom object which wraps the exception information and any other data that you desire.

Struts2 and servlet integration

i am getting data from action class to servlet by adding data to session.whenever i am clicking the item in select list onchange event is fired that function is invoked the our servlet up to now OK,whenever we send second time request that servlet is not called why? and also comparsion is failed it will maintain previous values only.here i am sending request from ajax.pls can any one provide solution ?
AjaX code
function verify_details()
{
var resourceId=document.getElementById("res").value
var url="/EIS10/ResourceTest?resourceId="+resourceId;
ajax(url);
}
Action class Code:
listResource=taskService.getUserList(taskId);
System.out.println("The list Of Resources are::"+listResource);
HttpSession session=request.getSession();
session.setAttribute("listResource", listResource);
ServletCode
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
System.out.println("Servlet is Called...........");
String resourceId=request.getParameter("resourceId");
boolean t=false;
System.out.println("Your Clicked Id::"+resourceId);
HttpSession session=request.getSession();
List l=(List)session.getAttribute("listResource");
System.out.println("Resource List in Servlet:"+l);
if(l!=null)
{
System.out.println("The Size of List::"+l.size());
Iterator itr=l.iterator();
while(itr.hasNext())
{
String s=itr.next().toString();
System.out.println("Elements in List:"+s);
if(s.equals(resourceId))
t=true;
}
response.setContentType("text/html");
if (t) {
response.getWriter().write("Y");
} else {
response.getWriter().write("N");
}
}
}
}
It's probably because the browser returns the contents from its cache at the second request. See http://spacebug.com/solving_browser_caching_problem_of_ajax-html/ for a solution, or use an AJAX library (jQuery for example) which can handle this for you.
Besides, if you're using Struts, why do you use a bare servlet to handle your AJAX call? Why don't you use a Struts action?

Resources