I want to create an abstract controller that will add the additional request mapping basen on actual mappings for the extending controller.
As an example, for the following controller
#Controller
public class MyController extends VariableResolvingController {
#RequestMapping("page.htm")
public void handlerMethod() {
}
}
I want it to extend VariableResolvingControllerthat will add a mapping to it's resolveContextVariable(...) method with the "page.htm.context" URI.
public abstract class VariableResolvingController {
public final #ResponseBody Object resolveContextVariable(String variableName) {
return "{}";
}
protected final void registerVariableResolver(String variableName, VariableResolver resolver) {
//...
}
}
This approach adds a possibility to resolve custom variables using eg. AJAX requests in a way almost transparent for a client code.
Do you know any existing solutions that would be appropriate in this case?
Solution: I achieved my goal by writing a custom HandlerMapping implementation (in essence a decorator for RequestMappingHandlerMapping).
One way of doing is add simple Servlet filter to your spring mvc
public class RequestCheckFilter implements Filter {
#Override
public void destroy() {
// ...
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
if (requestURI.endsWith(".context")) {
request.getRequestDispatcher(requestURI.concat(".htm"))
.forward(request,response);
} else {
chain.doFilter(httpServletRequest, response);
}
} catch (Exception ex) {
request.setAttribute("errorMessage", ex);
request.getRequestDispatcher("/WEB-INF/views/jsp/error.jsp")
.forward(request, response);
}
}
add it
public class MyWebInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
//...
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new RequestCheckFilter()};
}
}
or in web.xml
<filter>
<filter-name>reqHandlerFilter</filter-name>
<filter-class>RequestCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>reqHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Related
I am trying to create a jar file to inject into any of my spring boot project for logging the request details.
I am able to do this in one of my project. You can see the code below.
How to create the jar out of it and how to inject into other projects?
#Component
public class Interceptor extends HandlerInterceptorAdapter {
private static Logger log = LoggerFactory.getLogger(Interceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// log.info("Inside prehandle");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// log.info("Inside postHandle");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("Inside afterCompletion");
sendToLoggerApi(request, response);
}
}
#Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
#Autowired
Interceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(interceptor);
}
}
I am trying to add junit test case for my Spring Boot OncePerRequestFilter shouldNotFilter method logic. The logic works fine with real-time REST calls but junit case is failing. Any idea?.
Here is test code.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringFilterTest {
#Test
public void getHealthTest() throws Exception {
standaloneSetup(new PersonController()).addFilter(new SkipFilter()).build().perform(get("/health")).andExpect(status().isOk());
}
#Test
public void getPersonTest() throws Exception {
standaloneSetup(new PersonController()).addFilter(new SkipFilter()).build().perform(get("/person")).andExpect(status().isAccepted());
}
private class SkipFilter extends OncePerRequestFilter {
private Set<String> skipUrls = new HashSet<>(Arrays.asList("/health"));
private AntPathMatcher pathMatcher = new AntPathMatcher();
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
response.setStatus(HttpStatus.ACCEPTED.value());
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return skipUrls.stream().anyMatch(p -> pathMatcher.match(p, request.getServletPath()));
}
}
#RestController
#RequestMapping(value = "/")
private static class PersonController {
#GetMapping("person")
public void getPerson() {
}
#GetMapping("health")
public void getHealth() {
}
}
}
I am expecting both of junit #Test cases to be successful but health one is always failing(its using Filter).
Incase, if you want to replicate below is complete repo code.
https://github.com/imran9m/spring-filter-test
Below Expression evaluates to false with request.getServletPath() when /health
skipUrls.stream().anyMatch(p -> pathMatcher.match(p, request.getServletPath()));
Change to request.getRequestURI() to get the uri and below condition matches the path
skipUrls.stream().anyMatch(p -> pathMatcher.match(p, request.getRequestURI()));
I have several folders in /static/img/** and I need to add interceptors to some of them to check user permissions. I've used interceptors earlier and added them this way:
#SpringBootApplication
#EnableTransactionManagement
public class Application extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
#Bean
public AuthHeaderInterceptor authHeaderInterceptor() {
return new AuthHeaderInterceptor();
}
#Bean
public AuthCookieInterceptor authCookieInterceptor() {
return new AuthCookieInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(authHeaderInterceptor())
.addPathPatterns(REST_URL)
.excludePathPatterns(
new String[] {
REST_SECURITY_URL,
REST_SETTINGS_URL,
REST_REPORTS_URL
}
);
registry
.addInterceptor(authCookieInterceptor())
.addPathPatterns(REST_REPORTS_URL);
}
}
All works fine for rest controllers and their URLs, but now I need to secure some static resources and I added this:
#SpringBootApplication
#EnableTransactionManagement
public class Application extends WebMvcConfigurerAdapter {
...
#Bean
public RoleAdminInterceptor roleAdminInterceptor() {
return new RoleAdminInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(authHeaderInterceptor())
.addPathPatterns(REST_URL)
.excludePathPatterns(
new String[] {
REST_SECURITY_URL,
REST_SETTINGS_URL,
REST_REPORTS_URL
}
);
//THIS NOT WORK
registry
.addInterceptor(roleAdminInterceptor())
.addPathPatterns("/static/img/admin/**");
registry
.addInterceptor(authCookieInterceptor())
.addPathPatterns(REST_REPORTS_URL);
}
}
Commented line doesn't work. When I send request to /static/img/admin/test.png RoleAdminInterceptor is never called.
What I'm doing wrong?
I know this is an old question, but since it's unanswered it might help others searching for it.
This is what worked for me:
1- Declare an interceptor class:
class RoleBasedAccessInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
AntPathMatcher matcher = new AntPathMatcher();
String pattern = "/static/img/admin/**";
String requestURI = request.getRequestURI();
if (matcher.match(pattern, requestURI)) {
//Do whatever you need
return validateYourLogic();
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2- Configure WebMvcConfigurer
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RoleBasedAccessInterceptor());
}
}
I think in this case you could use Filters with Spring Security instead of Interceptors as you could Validate the access earlier on the process even before hitting the Interceptor, unless there is a specific use case that you need to use the interceptor here.
Some topic about the difference between these two:
filters-vs-interceptor
I'm overriding a method that belongs to super class. Method takes no parameter. Therefore, I'm unable to pass Objects to the View using Model. Anyone has any suggestion?
#Override
protected String connectView(){
// I'd like to include an object in Model here
// e.g. model.addAttribute(....)
// but unpossible because super does not take a Model as param
return "connect/status";
}
Without having at least the request object, I do not believe you can simply do what you are asking. However, there are a few options:
Use a servlet filter...add the required values to the session there
Use the Decorator Pattern, in which you would create a instance of your class (controller in this case) within another wrapper class. In your wrapper you would do the additional processing, then call the wrapped inner object (your wrapped controller), then do any final processing.
HandlerInterceptor like #sp00m suggests (although I have never used it, so I have no input there)
I am sure there are probably some other options out there, but I can't think of any others.
This should suit your needs, using a HandlerInterceptor, a custom annotation #Model and reflection.
Model.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Model {
}
RequestInterceptor.java
#Service
public class RequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
try {
Class<?> clazz = request.getClass();
if (clazz.isAnnotationPresent(Controller.class)) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Model.class)) {
field.set(request, new ModelMap());
break;
}
}
}
} catch (IllegalAccessException e) {
// log("Cannot access model field of controller " + clazz.getSimpleName());
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
try {
Class<?> clazz = request.getClass();
if (clazz.isAnnotationPresent(Controller.class)) {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Model.class)) {
ModelMap model = (ModelMap) field.get(request);
if (model != null) {
modelAndView.addAllObjects(model);
}
break;
}
}
}
} catch (IllegalAccessException e) {
// log("Cannot access model field of controller " + clazz.getSimpleName());
}
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
applicationContext.xml
<!-- register the interceptor -->
<mvc:interceptors>
<bean class="your.package.to.the.RequestInterceptor" />
</mvc:interceptors>
YourController.java
#Controller
public class YourController extends ConnectController {
#Model
private ModelMap model;
#Override
protected String connectView(){
// model is here available
model.addAttribute("attrName", "attrValue");
return "connect/status";
}
}
I would like to know how am I able to redirect the request inside the controller constructor if I need to do it?
For example: Inside the constructor I need to check some condition and if doesn't met I want to redirect to some other place. At the same way the rest of the constructor will not be executed neither the "original following action". I want the code like this.
#Controller
class SampleController{
public SampleController(){
if(![some condition]){
...redirecting code...
}
...rest code...
}
...rest code...
}
EDIT
If constructor is not a good option or approach then is there any option like before filter that will execute always before every action of a constructor and will redirect on the failure of some conditions?
You could use an interceptor:
public class CheckInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
if (handler instanceof TheController) {
// or for any controller: if (handler.getClass().isAnnotationPresent(Controller.class))
if (!check()) {
redirect("/check-failure.html");
return false;
}
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
private void redirect(HttpServletRequest request, HttpServletResponse response, String path) throws ServletException {
try {
response.sendRedirect(request.getContextPath() + path);
} catch (IOException e) {
throw new ServletException(e);
}
}
private boolean check() {
return ...
}
}
Then register it within the applicationContext.xml:
<mvc:interceptors>
<bean class="your.package.CheckInterceptor" />
</mvc:interceptors>