0

I'm currently working on a spring based web application and have a special requirement that seems not (at least not out of the box) be provided by spring MVC. The application serves data for multiple users each organized in their own "company". Once a user has logged in, I'm able to identify to which company he belongs to.

The application itself is built with multiple "modules", each with it's own domain objects, DAO, Service and Controller classes. The idea behind this concept is that I can for example extend a certain controller class (let's say to use a different service class) based upon the user and here is my problem.

Since i do not want to change my request paths for certain users, I'm currently looking for a way how to serve a request issued on a certain request path with different instances of a controller based upon the user issuing the request. I came up with the idea to attach a HTTP Header Field for the company Example:

X-Company:12345

and have my controllers configured like this:

@Controller
@RequestMapping(value="/foo/")
public class FooController { 
 // ...
}

@Controller
@RequestMapping(value="/foo" headers="X-Company=12345")
public class SpecialFooController extends FooController {
 // ...
}

However this is not possible, since spring MVC treats each header (except Content-Type and Accept) as a kind of restriction, so in my case it would handle all requests with the FooController instead of the SpecialFooController unless i add a "headers" restriction on the FooController as well, which is not practicable. Is there some way how to customize this behaviour or some direction one could point me to look for? Or maybe someone has another idea how to achieve this. It'll be highly appreciated. Thanks!

Markus
  • 1,016
  • 1
  • 8
  • 23
  • You can inject request headers in the controller method. So the request urls would be the same. Another option would be to use different subdomains for each company. – NilsH Apr 26 '13 at 19:32
  • Hmm... could you explain that approach a bit more? I'd thought about having a filter, so the company id would always be set, but this won't help me, since spring preferes to use the controller without the headers annotation. – Markus Apr 26 '13 at 19:36
  • Ok, I have added an answer trying to explain. – NilsH Apr 26 '13 at 19:44
  • I'm curious: why is adding a restriction to `FooController` not practicable? – a better oliver Apr 26 '13 at 19:51

3 Answers3

0

I'am not sure but I think you can do this with HandlerMapping. Have a look at the documentation

micha
  • 47,774
  • 16
  • 73
  • 80
0

To take your own suggestion, you can use the @RequestHeader annotation in your controller methods:

@Controller
public class MyController {

    @RequestMapping("/someAction")
    public void myControllerMethod(@RequestHeader('X-Company-Id') String companyId) {

    }
}

Or you could use @PathVariable:

@Controller
public class MyController {

    @RequestMapping("/{companyId}/someAction")
    public void myControllerMethod(@PathVariable("companyId") String companyId) {

    }
}

Using this approach would mean that it is in fact different URLs for each company, but if you can set the company id header, I guess you also can suffix the URLs with the company id.

But there are also other possibilities. You could write an interceptor that puts the company id in a session or request variable. Then you wouldn't have to add the annotation to every controller method. You could also use a subdomain for each company, but that wouldn't look too pretty if the company id is a random alphanumeric string. E.g: companyone.mydomain.com, companytwo.mydomain.com

Edit

@RequestMapping can be added to the controller level as you know, so you should be able to do

@Controller
@RequestMapping("/controller/{companyId}")

as the base url, if that's a better option.

NilsH
  • 13,705
  • 4
  • 41
  • 59
  • Ok, now I see. However I would like to avoid to clutter my controller code by dealing with those company id's, since I think this should be the Dispatchers work. – Markus Apr 26 '13 at 19:47
  • At some level I assume you would have to deal with the company ids? But you might want to look into an interceptor if you don't want to clutter your mappings with the company id. – NilsH Apr 26 '13 at 19:49
  • And of course, @RequestMapping can be added at controller level (did a small update of the answer). – NilsH Apr 26 '13 at 20:01
  • Well the company id should actually be used for routing my request to the correct Controller and Method. In your solution I would not be able to distinguish between different controllers, since the request will always be served by the same controller. – Markus Apr 26 '13 at 20:09
  • Aha. I think understand now. You probably would have to write custom logic for that. – NilsH Apr 26 '13 at 20:20
0

I was able to meet the requirement by making usage of a customized RequestCondition. By defining your own annotation that can be placed at the type and method level of a controller. Extending the RequestMappingHandlerMapping by your own implementation and overriding the getCustomTypeCondition() and getCustomMethodCondition() methods translates a controller annotation into your own RequestCondition.

When a request comes in, the custom RequestCondition will be evaluated and the annotated controller(method) will then be called to serve the request. However this has the downside, that one needs to remove a servlet-context.xml file and switch to the WebMvcConfigurationSupport class instead in order to be able to use your customized RequestMappingHandlerMapping class.

This question was also discussed here.

Edit: A pretty good example using this can be found here.

Community
  • 1
  • 1
Markus
  • 1,016
  • 1
  • 8
  • 23