Cony
Cony

Reputation: 223

Inject request attribute from request to spring controller methods

I have some spring @RestControllers methods that I would like to inject with a value that comes with every request as a request attribute(containing the user) something like:

@RestController
@RequestMapping("/api/jobs")
public class JobsController {
     // Option 1 get user from request attribute as prop somehow
     private String userId = "user1";

    // Option 2 inject into method using aspect or something else
    @RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Jobs>> getJobs() throws ResourceNotFoundException {
       // currentUser is injected 
       this.getJobs(currentUser);
}

I know I can do that:

@RestController
@RequestMapping("/api/jobs")
public class JobsController {

    @RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Jobs>> getJobs(HttpServletRequest request) throws ResourceNotFoundException { 
       String currentUser = null;
       if (request.getAttribute("subject") != null) {
           currentUser = request.getAttribute("subject").toString();
       }
       this.getJobs(currentUser);
}

But that would require me to add this code at every method in my program, which seems to me, to be a really bad practice.
Is there a way to achieve what I want?

If the answer do require aspect, a code example will be much appreciated since I only read about it, but never actually did something with aspect.

Update
The code i suggested can be simplified using this:

@RestController
@RequestMapping("/api/jobs")
public class JobsController {

    @RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<Jobs>> getJobs(@Value("#{request.getAttribute('subject')}" String currentUser) throws ResourceNotFoundException { 
       this.getJobs(currentUser);
}

But still require me to add that parameter at every method. Can this parameter be injected to every method somehow?

Upvotes: 2

Views: 14522

Answers (3)

user3805618
user3805618

Reputation: 21

Simply just add @RequestAttribute in your rest contorller

@RestController
@RequestMapping(path="/yourpath")

@RequestMapping(method = RequestMethod.GET)
    public ResponseEntity getAll(
            @RequestAttribute(value = "yourAttribute") Object 

yourAttribute......

Upvotes: 2

carcaret
carcaret

Reputation: 3358

You could use a Filter to populate a ThreadLocal<String> variable that stores that attribute:

public class MyFilter implements Filter {

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

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    ContextHolder.setSubject(request.getAttribute('subject'));
    chain.doFilter(request, response);
  }

  @Override
  public void destroy() {
    ContextHolder.removeSubject();
  }
}


public class ContextHolder {

  private static final ThreadLocal<String> SUBJECT = new ThreadLocal<String>() {
    @Override
    protected String initialValue() {
      return "empty";
    }
  };

  public static void setSubject(String subject) {
    SUBJECT.set(subject);
  }

  public static String getSubject() {
    return SUBJECT.get();
  }

  public static void removeSubject() {
    SUBJECT.remove();
  }
}

The filter will be configured to intercept all requests and populate the SUBJECT variable. By using a ThreadLocal, you make sure that each thread has it's own subject value. You can now get that value anywhere in your application by calling ContextHolder.getSubject():

  @RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<List<Jobs>> getJobs(HttpServletRequest request) throws ResourceNotFoundException { 
    this.getJobs(ContextHolder.getSubject());
  }

You will also have to register the Filter in the web.xml file:

<filter>
    <filter-name>MyFilter</filter-name>
    <filter-class>com.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

In case you had multiple attributes, you could use a ThreadLocal<Map<String, String>> variable instead.

Upvotes: 3

Software Engineer
Software Engineer

Reputation: 16110

If you really want to know about attributes then you should check out spring's @RequestParam annotation. You'd use it like this:

@RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Jobs>> getJobs(@RequestParam("subject") String currentUser) throws ResourceNotFoundException { 
   this.getJobs(currentUser);
}

Upvotes: 0

Related Questions