Is it possible in Spring to have one method with two different urls with different params for each method?
Below is pseudo code
#RequestMethod(URL1-param1, URL2-param2)
public void handleAction(#ModelAttribute("A") A a, ...) {
}
At the same time ULR1 is mapped in some other Controller as
#RequestMethod(URL1)
public void handleAction1(#ModelAttribute("A") A a, ...) {
}
Update: It appears your question is completely different.
No, you can't have the same url with different parameters in different controllers. And it doesn't make much sense - the url specifies a resource or action, and it cannot be named exactly the same way in two controllers (which denote different behaviours).
You have two options:
use different URLs
use one method in a misc controller that dispatches to the different controllers (which are injected) depending on the request param.
Original answer:
No. But you can have two methods that do the same thing:
#RequestMethod("/foo")
public void foo(#ModelAttribute("A") A a) {
foobar(a, null);
}
#RequestMethod("/bar")
public void bar(#ModelAttribute("B") B b) {
foobar(null, b);
}
If I haven't understood correctly, and you want the same ModelAttribute, then simply:
#RequestMapping(value={"/foo", "/bar"})
And finally - if you need different request parameters, you can use #RequestParam(required=false) to list all possible params.
you can supply multiple mappings for your handler like this
#RequestMapping(value={"", "/", "welcome"})
public void handleAction(#ModelAttribute("A") A a, ...) { }
But if you want to use different parameters for each mapping, then you have to extract your method.
Something like this
#RequestMapping(value={"URL1"}, method=RequestMethod.POST)
public String handleSubmit(#ModelAttribute("A") A command, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
return helperSubmit();
}
#RequestMapping(value={"URL2"}, method=RequestMethod.POST)
public String handleSubmit(#ModelAttribute("A") A command, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
return helperSubmit();
}
private helperSubmit() {
return "redirect:" + someUrl;
}
Related
Goal
I want to add an object SpecialHeaderBuilder to Spring methods (added using HandlerMethodArgumentResolver), which is then used by a HandlerInterceptor to actually add headers to the response in a very specific way.
Code created
I added the following class to my resolvers, so that I can now use SpecialHeaderBuilder instances in my methods.
public class SpecialHeaderBuilderArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(SpecialHeaderBuilder.class);
}
#Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
return new SpecialHeaderBuilder();
}
}
It is used as follows:
#RequestMapping("...")
public String someMethod(SpecialHeaderBuilder builder) {
// Do something with the builder
return "index"
}
Problem
Now I want to use that builder instance to actually set headers, so I created a HandlerInterceptor:
public class SpecialHeaderBuilderHandlerInterceptor implements HandlerInterceptor {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// How do I get the instance of the SpecialHeaderBuilder here?
}
}
I am not sure how to actually get the instance. I know I have the method signature, so I can see that the SpecialHeaderBuilder is actually part of the signature. I don't know how to access the builder argument that is modified inside the method. Does anybody know?
XY problem
I can actually change my response object to something like ResponseWithSpecialHeaders and use a HandlerInterceptor for that. However, I am curious if what I thought of as a solution could actually work. I would expect that it is possible, since the Model object works the same way. But maybe that is a special case.
New to Spring Boot here. Spring MVC provides the #SubdomainMapping annotation, which does not seem to be available from what I can see in Spring Boot. I've seen a few people discuss using a filter to handle this. Or other approaches that seem overly convoluted.
Would there not be a (simple/cleaner) way to handle all sub-domains within a standard controller such as:
#SubdomainMapping(value = {"**"}
public String data(ModelMap modelMap, HttpServletRequest request) {
//Code to handles subdomain logic here ....
}
This would be a simple approach where all values are treated equally with minor differences.
Any suggestions would be helpful!
I have worked on this myself and I have an answer that isn't as simple as you wanted, but I don't think there is one that simple.
So, you can create a handler interceptor adapter that will grab every request before it gets to your controller and grabs and processes the subdomain. This would require something like this:
#Component
public class SubDomainInterceptor extends HandlerInterceptorAdapter {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3)
throws Exception {
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView model)
throws Exception {
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
String mysub = request.getRequestURL().toString();
//...
// Do whatever you need to do with the subdomain
//
if (isGoodSubdomain){
session.sendAttribute("subdomain", mysub);
} else {
response.sendRedirect("http://www.basesite.com"):
}
return true;
}
You then use that session variable in your controllers to filter values or whatever you need to use them for. I know this isn't the simple answer you wanted, but it was the best one that I have found so far.
I want to implement high level resource filtering on URLs with a servlet filter and lower level action filtering on methods with an interceptor but my interceptor does not get fired on the EJB method called from the servlet filter.
Interceptor annotation Interface:
#Inherited
#InterceptorBinding
#Retention (RUNTIME)
#Target({TYPE, METHOD})
public #interface Permit {
#Nonbinding
String[] actions() default "N/A";
}
The Interceptor:
#Permit
#Interceptor
public class PermitInterceptor {
#AroundInvoke
public Object verifyPermission(InvocationContext context) throws Exception {
Method method = context.getMethod();
if(method.isAnnotationPresent(Permit.class)) {
Permit permitAnnotation = method.getAnnotation(Permit.class);
List<String> permittedActions = Arrays.asList(permitAnnotation.actions());
List<String> userActions = SecurityContextHandler.getActiveUser().getActions();
if(!Collections.disjoint(permittedActions, userActions)){
return context.proceed();
}
}
throw new PermissionException("You do NOT have the required permissions to perform this action");
}
}
Servlet Filter:
#WebFilter(urlPatterns = {"/*"})
public class AccessFilter implements Filter {
#EJB
private RulesBean rules;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURI();
if(rules.isAllowed(url)){
chain.doFilter(request, response);
}else{
//handle as necessary
}
}catch(Exception ex){
//handle as necessary
}
}
}
And finally here's what the EJB RulesBean that I want to use to manage routing/interception for all my servlets looks like;
Rules:
#Stateless
#LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return canViewAllCustomers();
default: return true;
}
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
#Permit(actions={"ViewCustomers"})
private boolean canViewAllCustomers(){
return true;
}
...
//Other tests carried out here ...
}
Unfortunately PermitInterceptor doesn't get called before entering canViewAllCustomers() method.
Amazingly however, PermitInterceptor gets triggered when canViewAllCustomers() is made public and called directly as rules.canViewAllCustomers() instead of through the helper method rules.isAllowed(String url). But this isn't helpful in my case, as it gives me no single entry point for my URL checks which essentially means I have to do all the checks in the Servlet Filter.
QUESTION: Please can anybody shed more light on the reason why things are occurring in this manner?... and suggestions about the best way to implement this scenario is highly welcome. Thanks.
NOTE: (To give more perspective)
You may be wondering why I want to do this OR more specifically why the RuleBean even exists at all... The reason is simply because a good number of my Servlets aren't doing much except route response to a view that triggers a server-side DataTables ajax call which populates the tables, hence I really need to ensure that the request for the view doesn't even get through to the if...else condition that fetches the view unless the permission checks in the interceptor is satisfied.
See sample servlet below;
#WebServlet ("/customers/*")
public class CustomerServlet extends VelocityViewServlet {
private static final String CUSTOMERS = "/customers"
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
if(uri.equals(CUSTOMERS)){
String view = Path.getTemplate("/customers/index.vm");
request.setAttribute("headerTitle", "Customers");
request.getRequestDispatcher(view).forward(request, response);
}else if(...){
...
}
}
}
You invoke canViewAllCustomers() within isAllowed() directly, which gives the Application Server no chance to intercept the call.
Interception works with proxy classes. When you inject the EJB into your servlet, like you did with:
#EJB
private RulesBean rules;
what actually gets injected is not an EJB instance, but a proxy class, that the application server created at runtime (you can see this with the debugger). Invocations on that proxy class will be intercepted for transactions, custom interceptors, etc. and then delegated to the actual class instance.
So what you need to do is either put canViewAllCustomers() into a new EJB, that you can let the application server inject into your RulesBean class,
or you can retrieve a reference of your RulesBean class from inside isAllowed() like so:
#Stateless
#LocalBean
public class RulesBean {
private static final String CUSTOMERS = "/customers"
#Resource
SessionContext ctx;
public boolean isAllowed(String url) throws PermissionException {
switch(url){
case CUSTOMERS: return self().canViewAllCustomers();
default: return true;
}
}
private RulesBean self() {
return ctx.getBusinessObject(RulesBean.class);
}
/*This should trigger PermitInterceptor before entering method and
should throw my custom PermissionException if permission fails*/
#Permit(actions={"ViewCustomers"})
public boolean canViewAllCustomers(){
return true;
}
}
I have some common components that are always present in every page served by a given Controller class.
At the beginning of each #RequestMapping method I populate the model with these common components.
Is there a way to define a method be called prior to each of the controller methods so that I can get all of this copy/paste into one place?
Just annotate a method with #ModelAttribute
The below would add a Foo instance to the model under the name "foo"
#ModelAttribute("foo")
public Foo foo() {
return new Foo();
}
See the #ModelAttribute documentation
Interceptor is the solution. It has methods preHandler and postHandler, which will be called before and after each request respectively. You can hook into each HTTPServletRequest object and also by pass few by digging it.
here is a sample code:
#Component
public class AuthCodeInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// set few parameters to handle ajax request from different host
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.addHeader("Access-Control-Max-Age", "1000");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Cache-Control", "private");
String reqUri = request.getRequestURI();
String serviceName = reqUri.substring(reqUri.lastIndexOf("/") + 1,
reqUri.length());
if (serviceName.equals("SOMETHING")) {
}
return super.preHandle(request, response, handler);
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
}
All methods that have the #ModelAttribute annotation are called before the specific handler and the return values are added to the Model instance. Then you can use this attributes in your views and as handler parameters.
I found this blog very useful.
Yes, you can use an interceptor. You can define them by <mvc:interceptors>
Another option is to use s Filter, but you won't be able to inject spring beans into it.
Another approach would be to annotate the controller class as request-scoped (#Scope('request')) so that every request will create a new instance of the controller to invoke the matching method on it.
You can then put all your pre-processing work into a post-construct method (i.e. a normal method annotated with #PostConstruct) which will always be called after a new controller instance is initialized (i.e created and all dependencies are resolved) and before the request-matching method is invoked.
I suppose that this would be a bit inefficient if controller's initialization is heavy (e.g. costly computations or many dependencies to resolve); but yet is another approach to this problem.
I have a servlet that correctly returns the data I want from the database when it's doGet() method is called. I would like the doGet() to populate a java bean, which we then reference in the product.jsf page.
I'd like call a URL like http://example.com/product.jsf?id=XXX
And have the single record returned based on the ID based in the URL. I can't figure this out.
Don't you need to declare a field of the bean with the same name as a field on the page? Most MVC engines can keep the two in sync if they follow the proper naming convention.
That servlet has too much responsibilities. It is tight coupled. Refactor the data access code into a separate class so that you can reuse it in both the servlet class and the JSF bean.
So, basically:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Long id = Long.valueOf(request.getParameter("id"));
List<Product> products = productDAO.list(id);
request.setAttribute("products", products);
request.getRequestDispatcher("products.jsp").forward(request, response);
}
And:
#ManagedProperty(value="#{param.id}")
private Long id;
private List<Product> products; // +getter
#PostConstruct
public void init() {
this.products = productDAO.list(id);
}
So that they can operate independently.