How to know which Rest controller method called using HttpServletRequest or HttpServletResponse object? - spring-boot

I am using HandlerInterceptor (import org.springframework.web.servlet.HandlerInterceptor;) to get the Request and Response Attributes and Header information using preHandle, postHandle methods.
Now I want to know the name of the method called of Controller. Is there any way if we can get that info using HttpServletRequest or HttpServletResponse object ?

We need to use Object handler to get the details of method invocation. See the below code:
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
and
public static String getMethodName(Object handler) {
String methodName = null;
if(handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
methodName = method.getMethod().getName();
}
return methodName;
}

Related

Why is a GET request mapped to "/error" in Spring-Boot?

When I send a GET request to /todo-list/user/list
it was mapped to /error.
Code
Interceptor:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
RequestMapping classMapping = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequestMapping.class);
// when debugging: classMapping.value() was "${server.error.path:${error.path:/error}}"
RequestMapping methodMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
//some other codes
Controller:
//the contexPath is "/todo-list"
#RestController
#RequestMapping("/user")
#RequiredArgsConstructor
public class UserController
Method
#GetMapping(value = "/list")
public #ResponseBody Page<UserProjection> listGrid() {
return userService.listGrid();
}
I checked the address and method and whatever I think is the reason.
When debugging my code, classMapping.value() was equal to "${server.error.path:${error.path:/error}}".
What is the problem?

How to make a controller for all extension type requests?

Have a spring boot project and a default controller:
#Controller
public class GenericController
{
#RequestMapping(value= {"/**.html", "/"})
public String httpRequest(Model model, HttpServletRequest request)
{
But works only with /*.html routes. How to catch all .html routes with any folder source? example: /abc.html, /abc/def.html, /abc/def/ghi.html, etc.
I learn about:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#path--
Learning Ant path style
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html
And try with:
#RequestMapping(value= {"/**/*.html", "/"})
But does not works, when call http://localhost/abc/def/ghi.html returns an http status 404.
I don't know why you want to do that but you can hack path params to do it for you. But its a dirty way and can cause conflicts with other mappings.
By using path params like below you can do /abc.html, /abc/def.html, /abc/def/ghi.html.
#RequestMapping(value = { "/**.html" , "/{path2}/**.html" ,"/{path}/{path2}/**.html" })
public String httpRequest(Model model) {
//You can also check which path variables are present and work accordingly
System.out.println("index");
return "index";
}
If you want to create a single entry point for your API then I would suggest you to read about GraphQL
Another approach can be using a Filter, that redirects your response according to incoming URI:
#Component
#Order(1)
public class AFilter implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
if(httpServletRequest.getRequestURI()...){ // use regex ?
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
((HttpServletResponse) servletResponse).sendRedirect("/some/path/to/your/thingy");
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
And some controller:
#RequestMapping(value = "/some/path/to/your/thingy", method = RequestMethod.GET)
public ResponseEntity<Object> aMethod() throws Exception {
return ResponseEntity.ok("ok");
}

Spring-MVC Exception handler returns OK when writing into response

I'm using spring-webmvc : 3.2.3.RELEASE (and its related dependencies).
I have this controller:
#Controller
#RequestMapping("/home")
public class HomeController {
#Autowired
MappingJacksonHttpMessageConverter messageConverter;
#RequestMapping(method = RequestMethod.GET)
public String get() {
throw new RuntimeException("XXXXXX");
}
#ExceptionHandler(value = java.lang.RuntimeException.class)
#ResponseStatus(HttpStatus.CONFLICT)
public ModelAndView runtimeExceptionAndView(ServletWebRequest webRequest) throws Exception {
ModelAndView retVal = handleResponseBody("AASASAS", webRequest);
return retVal;
}
#SuppressWarnings({ "resource", "rawtypes", "unchecked" })
private ModelAndView handleResponseBody(Object body, ServletWebRequest webRequest) throws ServletException, IOException {
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
messageConverter.write(body, MediaType.APPLICATION_JSON, outputMessage);
return new ModelAndView();
}
}
since the "/home" method throws RuntimeException that is being handled with the #ExceptionHandler, when the get() method is invoked, I'm expectin to get HttpStatus.CONFLICT, but instead, I'm getting HttpStatus.OK.
Can someone please tell me what should I do in order to get the response status from
the annotated exception handler?
The reason is because you are explicitly writing to the output stream, instead of letting the framework handle it. The header has to go before the body content is written, if you are explicitly handling writing to the output stream, you will have to write the header also yourself.
To let the framework handle the entire flow, you can instead do this:
#ExceptionHandler(value = java.lang.RuntimeException.class)
#ResponseStatus(HttpStatus.CONFLICT)
#ResponseBody
public TypeToBeMarshalled runtimeExceptionAndView(ServletWebRequest webRequest) throws Exception {
return typeToBeMarshalled;
}
Modify ExceptionHandler method like this
#ExceptionHandler(value = java.lang.RuntimeException.class)
public ModelAndView runtimeExceptionAndView(ServletWebRequest webRequest, HttpServletResponse response) throws Exception {
response.setStatus(HttpStatus.CONFLICT.value());
ModelAndView retVal = handleResponseBody("AASASAS", webRequest);
return retVal;
}
If you want to handle exception by json result, I suggest to use #ResponseBody with Automatic Json return.
#ExceptionHandler(value = java.lang.RuntimeException.class)
#ResponseBody
public Object runtimeExceptionAndView(ServletWebRequest webRequest, HttpServletResponse response) throws Exception {
response.setStatus(HttpStatus.CONFLICT.value());
return new JsonResult();
}

how to get returned value of my controllers from HandlerInterceptor

I'm creating a log manager for my controllers that logs every action in it and returned values
My controllers are defined in this way:
#Controller
#RequestMapping(value="/ajax/user")
public class UserController extends AbstractController{
#RequestMapping(value="/signup")
public #ResponseBody ActionResponse signup(#Valid SignupModel sign) {
ActionResponse response=new ActionRespone();
response.setMessage("This is a test message");
return response;
}
}
and I defined a HandlerInterceptor to log output of each handler:
#Component
public class ControllerInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
LogManager log=new LogManager();
log.setMessage();//I need returned ActionResponse here
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
where I use log.setMessage(); I need my ActionResponse's message (This is a test message) which is returned from signup method
How can I do this?
An interceptor is not the right place to do what you want since it's not capable of getting the return value of the handler.
You can achieve what you wan't without changing any existing code using aspect oriented programming (AOP). For this to work in spring you'll need to include the jars for spring-aop and AspectJ.
Creating the aspect and advice
#Aspect
#Component
public class ActionResponseLoggerAspect {
private static final Logger logger = LoggerFactory.getLogger(ActionResponseLoggerAspect.class);
#AfterReturning(pointcut="execution(* your.package.UserController.*(..)))", returning="result")
public void afterReturning(JoinPoint joinPoint , Object result) {
if (result instanceof ActionResponse) {
ActionResponse m = (ActionResponse) result;
logger.info("ActionResponse returned with message [{}]", m.getMessage());
}
}
}
The afterReturning method will be executed every time a controller method returns.
Enabling #AspectJ Support
Enable AspectJ support by adding this to your XML configuration.
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
For more info see the spring docs.

Modify request URI in spring mvc

I have a spring mvc based application. I want to modify the request URI before it reaches controller. For example, RequestMapping for controller is "abc/xyz" but the request coming is "abc/1/xyz". I want to modify incoming request to map it to controller.
Solution1: Implement interceptor and modify incoming request URI. But the problem here is that as there is no controller matching the URI pattern "abc/1/xyz", it does not even goes to interceptor.(I might be missing something to enable it if its there)
Get around for it could be to have both of URI as request mapping for controller.
What other solutions could be there? Is there a way to handle this request even before it comes to spring. As in handle it at filter in web.xml, i am just making it up.
You could write a servlet Filter which wraps the HttpServletRequest and returns a different value for the method getRequestURI. Something like that:
public class RequestURIOverriderServletFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
#Override
public String getRequestURI() {
// return what you want
}
}, response);
}
// ...
}
The servlet filter configuration must be added into the web.xml.
But sincerly, there is probably other way to solve your problems and you should not do this unless you have very good reasons.
in order to achieve this you should replace every place that affected when you calling uri.
the place that not mentioned is INCLUDE_SERVLET_PATH_ATTRIBUTE which is internally is accessed when going deeper.
public class AuthFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
private final String API_PREFIX = "/api";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
if (requestURI.startsWith(API_PREFIX)) {
String redirectURI = requestURI.substring(API_PREFIX.length());
StringBuffer redirectURL = new StringBuffer(((HttpServletRequest) request).getRequestURL().toString().replaceFirst(API_PREFIX, ""));
filterChain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
#Override
public String getRequestURI() {
return redirectURI;
}
#Override
public StringBuffer getRequestURL() {
return redirectURL;
}
#Override
public Object getAttribute(String name) {
if(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE.equals(name))
return redirectURI;
return super.getAttribute(name);
}
}, response);
} else {
filterChain.doFilter(request, response);
}
}
}
You can use a URL Re-Write which are specifically meant for this purpose i.e. transform one request URI to another URI based on some regex.

Resources