How to simplify the request mappings of these controllers? - java

I've a controller class with the following signature:
#Controller
#RequestMapping("/home/users")
public class MaterialsController { ... }
and every method in this controller class starts with the same path variable, that is, {username}, for example, I've the following method:
#RequestMapping(value = "{username}/mycreations/{coursewareName}/materials/{materialId}", method = RequestMethod.DELETE)
public void deleteCourseMaterialFromCreatedCourseware(#PathVariable String username,
#PathVariable String coursewareName, #PathVariable String materialId, Model model,
HttpServletRequest request, HttpServletResponse response) {
Now, it's a little bit tedious to write {username} at the beginning of each of these controller's methods. Is there a way to specify this path variable at class level? Of course I also need to access it then, like in the same way I'm specifying #PathVariable String username in the method above.
Also, I ideally, I would like also to include at class-level more than one variable, when necessary.

Yes, you can put it at the class level, but you have to include it in the path variable in the appropriate methods.
Can I use path variable in spring controller class?

Related

Bind #PostMapping to multiple parameters instead of model bean?

When using #GetMapping, I could bind each get-query parameter to one method parameter with #RequestParam annotation.
The following does not work, it would only be valid with #GetMapping:
//#PostMapping("/search")
#GetMapping("/search")
public void search(#RequestParam String origin, #RequestParam destination) {
}
Question: how can I achieve the same with #PostMapping?
Or do I always have to use a model bean like:
#PostMapping("/search")
public void search(#RequestBody model) {
}
The two ways are different, if the payload contains an object representing a serializable entity you should go for the second way and let jackson handle the deserialization for you, if not you can use the first one or you can build an entity for that , both works

#Path in rest-easy with variable?

I am using rest-easy and I want my #Path annotation to obtain it's value from a variable (mayabe a system parameter).
Eg:
#Path(someVar)
#GET
#Produces(MediaType.TEXT_XML)
public String retrieve() {
}
I tried reading and got to know that Path must be a constant value.
Is something like above possible in rest easy ??
Annotations are processed on compile time. You cannot use a variable as an annotation parameter.
You can pass variable value in URL, something like below example
so your URL will be /book/some_isbn_code and the getBook method will receive isbn value in id field.
#Path("/book/{isbn}")
public String getBook(#PathParam("isbn") String id) {

Evaluate pathparam arguments in Jersey using AspectJ

I have several APIs which retain a parameter "feature" from the url (path param). To avoid retrieving it in each method endpoint (eg.)
#GET
public void findAll(#PathParam("feature") String feature);
am trying to implement AOP using AspectJ.
Following is the implementation of the Aspect
#Aspect
public class FeatureAOP {
#Pointcut("execution(* x.y.z.rest.ModifiersFacadeWrapper.*(..)) && args(feature)")
public void pointCut(String feature) {
}
#Before("x.y.z.rest.aop.FeatureAOP.pointCut(feature)")
public void parseParams(JoinPoint jp, String feature) {
Object[] x = jp.getArgs();
System.out.println("Feature: " + feature);
}
}
The above method gives me the value of "feature" in the Aspect class but if I change the method findAll to following signature, it doesn't works.
#GET
public void findAll();
What I understand is the control is transferred to the Aspect after the parameters are resolved and removing it from the method definition is failing it.
Doing so, thus takes me to the same point where I have to define all method endpoints with the parameter in its signature. I would like to know if there is a way I can get the PathParams in the Aspect class without having to define my methods with the designated parameters.
I think you could probably do it by putting the resolved params in a globally accessible data structure (e.g. a Singleton having some sort of Map or Set), but
I wouldn't recommend that kind of approach. I don't know why you don't like having all the params in your method signatures, but that is the intended way of declaring rest services, e.g.
#GET
#Path("{feature}")
#Produces("text/plain")
public String getFeature(#PathParam("feature") String feature) {
return feature;
}
This way you don't have to write any code for retrieving the params, the rest library you are using (be it Jersey or a different one) will just do everything for you.

Override #RequestMapping method using inheritance

In my Java Spring MVC 4 project, I have an AbstractRESTController with an update method:
#RequestMapping(
value="/{id}",
method=RequestMethod.PUT,
consumes={MediaType.APPLICATION_JSON_VALUE}
)
public #ResponseBody ResponseEntity<T> update(#PathVariable ID id,
#RequestParam String token, #RequestBody T json) {
[do fancy stuff]
}
and an extending class, let's call it MyController. Usually I want to use the method from the abstract class, but in MyController I have a special case (yay!), so I need to do further work.
My idea was to just override the #RequestMapping in the child class, do my additional fancy stuff and afterwards call the super class' update method from the MyController.update method. But this does not work, because I get an ambiguous mapping error during compilation.
Is there a way to make Spring override the parent class request mapping? I would like to avoid splitting the routes.
As you have noticed you can't do this because the ambiguous mapping.
If you want execute some additional code, you can use something like hook methods. So, define in your AbstractRESTController an empty method like this:
protected void doFancyStuff() {
}
Obs.: the empty method is a better choice here, and not an abstract one, to avoid the need to implement even with empty method body in all concrete controller.
Change the update method to call the hook method:
#RequestMapping(
value="/{id}",
method=RequestMethod.PUT,
consumes={MediaType.APPLICATION_JSON_VALUE}
)
public #ResponseBody ResponseEntity<T> update(#PathVariable ID id,
#RequestParam String token, #RequestBody T json) {
doFancyStuff();
}
And in MyController you will override and implement doFancyStuff method.

Spring #ModelAttribute and translating request parameter binding names

I'm working on converting a legacy project to Spring (trying to adjust little as possible for now) and I'm running into a small issue with mapping/translating legacy parameters to a model attribute object. I may be completely wrong in thinking about this problem but it appears to me that to translate a parameter to a specific model attribute setter is to pass in the request parameter through a method for creating a model attribute and manually call the correct setter:
#ModelAttribute("form")
public MyForm createMyForm(#RequestParameter("legacy-param") legacy) {
MyForm myForm = new MyForm();
myForm.setNewParam(legacy);
return myForm;
}
I don't necessarily want to change the request parameter name yet since some javascript and JSPs are depending on it being named that way but is there any way to do something like this? Or is there a different way to map/translate request parameters to model attributes?
public class MyForm {
#ParameterName("legacy-param")
private String newParam;
public void setNewParam(String value) { ... }
public String getNewParam() { ... }
}
#Controller
public class MyController {
#RequestMapping("/a/url")
public String myMethod(#ModelAttribute("form") MyForm myForm, BindingResult result) { ... }
}
The way you've written that model attribute method is indeed odd. I'm not entirely clear what you're actually trying to do.Assuming there are many parameters, you're going to end up with an awful lot of instances of MyForm in your ModelMap. A more 'normal' way to create model attribute would be like this:
#ModelAttribute("legacyParamNotCamel")
public MyForm createMyForm(#RequestParameter("legacy-param-not-camel") String legacy) {
return legacy;
}
Then in the JSP you can refer to it directly in expression language. e.g.,
<c:out value="${legacyParamNotCamel}"/>
If you want to put them onto a form backing object, you need to do it all in a single method that creates the object, not make new copies of it in each method. (assuming your form has more than a single parameter associated with it.)
--
It seems like what you're really trying to do though is translate the parameter names in the request before the web data binder gets ahold of it, so that you can bind oddly named parameters onto a java bean? For that you'll need to use an interceptor that translates the names before the binding process begins, or make your own subclass of the databinder than can take a property name translation map.
You placed the #ModelAttribute at the Method Level but the intention seems to be more of a formBackingObject hence we should be dealing at the Method Parameter Level
There's a difference.
I put up an explanation here on my blog along examples at Spring 3 MVC: Using #ModelAttribute in Your JSPs at http://krams915.blogspot.com/2010/12/spring-3-mvc-using-modelattribute-in.html

Categories