How to check if PathVariable in the URI in Spring MVC's request mapping? - spring

In my controller there is such code.
#RequestMapping(value = "/{scene}/{function}/**")
public void processProxyCall(#PathVariable("scene") final String scene, #PathVariable("function") final String function,
final HttpServletRequest request,final HttpServletResponse response) throws IOException {
...
}
In testing phase, where the possible values of {scene} is "sit" or "uat". And there are some additional logic to handle this variable in the phase.
So it's well fit for such URI "/sit/student/add". The scene is sit, and the function is student.
But in production there isn't any "sit" nor "uat" anymore , the URI in this case will be "/student/add". There is no need to handle the scene variable either.
The question is how to do some checking against the PathVariable "scene" in above code snippet. If production case the scene will be automatically mapped to "student" which is terribly wrong.
I was trying to add another RequestMapping as below to handle the production case, and remain the testing one no change. But got 404...
#RequestMapping(value = "/{function}/**")
public void processProxyCall(#PathVariable("function") final String function, final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
}

#RequestMapping(value = {"/{scene}/{function}/**", "/{function}/**"})
public void processProxyCall(#PathVariable(value="scene", required=false) final String scene, #PathVariable("function") final String function,
final HttpServletRequest request,final HttpServletResponse response) throws IOException {
...
}
Define multiple mappings and make scene path variable required=false
UPDATE:
#RequestMapping(value = "/{scene}/{function}/**")
public void processProxyCall(#PathVariable(value="scene", required=false) final String scene, #PathVariable("function") final String function,
final HttpServletRequest request,final HttpServletResponse response) throws IOException {
...
}
#RequestMapping(value = "/{function}/**")
public void processProxyCallShort(#PathVariable(value="scene", required=false) final String scene, #PathVariable("function") final String function,
final HttpServletRequest request,final HttpServletResponse response) throws IOException {
processProxyCall(null, function, request, response);
}

Related

Send status 200 without having to wait any return

I have a GET request in my API that can be called via url. My problem is that this request is waiting for some return, even though it may take indefinitely. I would like to return a 200 status code right at the beginning of the application so that the user does not have his page blocked waiting for a response, while the rest of the code is executed normally.
My actual code look like this:
#Controller
public class APITest {
#RequestMapping(value="test", method=RequestMethod.GET)
public void RequestTest(
#RequestParam(value="token", required=false) String token,
HttpServletRequest request, HttpServletResponse response)
throws InterruptedException, ParseException, IOException, SQLException {
// SOME CODE HERE
return;
}
}
Is what I need possible using this method?
You could run the code in a different thread.
#Controller
public class APITest {
#RequestMapping(value="test", method=RequestMethod.GET)
public void RequestTest(
#RequestParam(value="token", required=false) String token,
HttpServletRequest request, HttpServletResponse response)
throws InterruptedException, ParseException, IOException, SQLException {
CompletableFuture.runAsync(() -> longRunningTask());
return;
}
}
The longRunningTask() will be executed in a different thread and RequestTest() will return directly with a 200.

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

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

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 Boot: Inject a custom context path

I'm running a Spring Boot 1.2.3 application with embedded Tomcat.
I'd like to inject a custom contextPath on every request, based on the first part of the URL.
Examples:
http://localhost:8080/foo has by default contextPath="" and should get contextPath="foo"
http://localhost:8080/foo/bar has by default contextPath="" and should get contextPath="foo"
(URLs without path should stay as is)
I tried to write a custom javax.servlet.Filter with #Order(Ordered.HIGHEST_PRECEDENCE), but it seems like I'm missing something. Here's the code:
#Component #Order(Ordered.HIGHEST_PRECEDENCE)
public class MultiTenancyFilter implements Filter {
private final static Pattern pattern = Pattern.compile("^/(?<contextpath>[^/]+).*$");
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
final String requestURI = req.getRequestURI();
Matcher matcher = pattern.matcher(requestURI);
if(matcher.matches()) {
chain.doFilter(new HttpServletRequestWrapper(req) {
#Override
public String getContextPath() {
return "/"+matcher.group("contextpath");
}
}, response);
}
}
#Override public void init(FilterConfig filterConfig) throws ServletException {}
#Override public void destroy() {}
}
This should simply take the String after the first / and before (if any) the second / and then use it as return value for getContextPath().
But Spring #Controller #RequestMapping and Spring Security's antMatchers("/") does not seem to respect it. Both still work as if contextPath="".
How can I dynamically override the context path for each request?
Got it working!
Spring Security docs ( http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html ) say: "Spring Security is only interested in securing paths within the application, so the contextPath is ignored. Unfortunately, the servlet spec does not define exactly what the values of servletPath and pathInfo will contain for a particular request URI. [...] The strategy is implemented in the class AntPathRequestMatcher which uses Spring's AntPathMatcher to perform a case-insensitive match of the pattern against the concatenated servletPath and pathInfo, ignoring the queryString."
So I just did override servletPath and contextPath (even if it's not used by Spring Security). Additionally I added some small redirect, because normally when hitting http://localhost:8080/myContext you get redirected to http://localhost:8080/myContext/ and Spring Securities Ant Matcher did not like the missing trailing slash.
So here's my MultiTenancyFilter code:
#Component #Order(Ordered.HIGHEST_PRECEDENCE)
public class MultiTenancyFilter extends OncePerRequestFilter {
private final static Pattern pattern = Pattern.compile("^(?<contextPath>/[^/]+)(?<servletPath>.*)$");
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Matcher matcher = pattern.matcher(request.getServletPath());
if(matcher.matches()) {
final String contextPath = matcher.group("contextPath");
final String servletPath = matcher.group("servletPath");
if(servletPath.trim().isEmpty()) {
response.sendRedirect(contextPath+"/");
return;
}
filterChain.doFilter(new HttpServletRequestWrapper(request) {
#Override
public String getContextPath() {
return contextPath;
}
#Override
public String getServletPath() {
return servletPath;
}
}, response);
} else {
filterChain.doFilter(request, response);
}
}
#Override
protected String getAlreadyFilteredAttributeName() {
return "multiTenancyFilter" + OncePerRequestFilter.ALREADY_FILTERED_SUFFIX;
}
}
It simply extracts the contextPath and servletPath using the URL schema mentioned here: https://theholyjava.wordpress.com/2014/03/24/httpservletrequest-requesturirequesturlcontextpathservletpathpathinfoquerystring/
Additionally I had to provide a custom getAlreadyFilteredAttributeName method, because else the filter got called twice. (This resulted in stripping the contextPath twice)

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