17

I'm trying to add a Filter that creates an object that is then to be used inside a controller in a Spring Boot application.

The idea is to use the Filter as a "centralized" generator of this object - that is request-specific and useful only in a controller. I've tried to use the HttpServletRequest request.getSession().setAttribute method: I can access my object in the controller, but then it will be (clearly) added to the session.

Are the Filters the right way to do so? If yes, where can I keep the temporary object generated by the filter to be used by the controllers?

Roberto
  • 2,206
  • 4
  • 24
  • 31

4 Answers4

30

Why Don't you use a Bean with the @Scope('request')

@Component
@Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
class UserInfo {
   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   private String password;
}

and then you can Autowireed this bean in both filter and controller to do setting and getting of data.

lifecycle of this UserInfo bean is only exisits within the request so once the http request is done then it terminates the instance as well

CuriousGeorge
  • 7,120
  • 6
  • 42
  • 74
Imal Hasaranga Perera
  • 9,683
  • 3
  • 51
  • 41
18

you can use ServletRequest.setAttribute(String name, Object o);

for example

@RestController
@EnableAutoConfiguration
public class App {

    @RequestMapping("/")
    public String index(HttpServletRequest httpServletRequest) {
        return (String) httpServletRequest.getAttribute(MyFilter.passKey);
    }

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @Component
    public static class MyFilter implements Filter {

        public static String passKey = "passKey";

        private static String passValue = "hello world";

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {

        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            request.setAttribute(passKey, passValue);
            chain.doFilter(request, response);
        }

        @Override
        public void destroy() {

        }
    }
}
wcong
  • 591
  • 4
  • 8
7

An addition to wcong's answer. Since Spring 4.3 after setting the attribute by using request.setAttribute(passKey, passValue);, you can access the attribute in your controller by simply annotating it with @RequestAttribute.

ex.

@RequestMapping("/")
public String index(@RequestAttribute passKey) {
    return (String) passKey;
}
stanojevicn
  • 71
  • 1
  • 3
3

I dont know actually what is the scenario but If you really want to create an object in a filter and then use it somewhere in the code then you may use ThreadLocal class to do so.

To get know how this work see the most voted answer from that question Purpose of ThreadLocal?

In general using ThreadLocal you will be able to create a class that can store objects available ONLY for the current thread.

Sometimes for optimization reasons the same thread can be used to serve subsequent request as well so it will be nice to clean the threadLocal value after the request is processed.

class MyObjectStorage {
  static private ThreadLocal threadLocal = new ThreadLocal<MyObject>();

  static ThreadLocal<MyObject> getThreadLocal() {
    return threadLocal;
  }
}

in the filter

MyObjectStorage.getThreadLocal().set(myObject);

and in the Controller

MyObjectStorage.getThreadLocal().get();

Instead of filter you can use also @ControllerAdvice and pass objects to specified Controllers by using model.

@ControllerAdvice(assignableTypes={MyController.class})
class AddMyObjectAdvice {

    // if you need request parameters
    private @Inject HttpServletRequest request; 

    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("myObject", myObject);
    }
}


@Controller
public class MyController{

   @RequestMapping(value = "/anyMethod", method = RequestMethod.POST)
   public String anyMethod(Model model) {
      MyObjecte myObject = model.getAttribute("myObject");

      return "result";
   }
}
Community
  • 1
  • 1
Marek Raki
  • 3,056
  • 3
  • 27
  • 50
  • 1
    Usingt thread local isn't a very good practice https://plumbr.io/blog/locked-threads/how-to-shoot-yourself-in-foot-with-threadlocals – Vinz243 Nov 19 '19 at 15:01