Thanks Alexey for leading the way.
His solution is:
- Add a @ControllerAdvice triggering for all controllers, or selected ones
- This @ControllerAdvice has a @PathVariable (for a "/path/{variable}" URL) or a @RequestParam (for a "?variable=..." in URL) to get the ID from the request (worth mentioning both annotations to avoid blind-"copy/past bug", true story ;-) )
- This @ControllerAdvice then populates a model attribute with the data fetched from database (for instance)
- The controllers with uses @ModelAttribute as method parameters to retrieve the data from the current request's model
I'd like to add a warning and a more complete example:
Warning: see JavaDoc for ModelAttribute.name() if no name is provided to the @ModelAttribute annotation (better to not clutter the code):
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name:
e.g. "orderAddress" for class "mypackage.OrderAddress",
or "orderAddressList" for "List<mypackage.OrderAddress>".
The complete example:
@ControllerAdvice
public class ParentInjector {
@ModelAttribute
public void injectParent(@PathVariable long parentId, Model model) {
model.addAttribute("parentDTO", new ParentDTO(parentId, "A faked parent"));
}
}
@RestController
@RequestMapping("/api/parents/{parentId:[0-9]+}/childs")
public class ChildResource {
@GetMapping("/{childId:[0-9]+}")
public ChildDTO getOne(@ModelAttribute ParentDTO parent, long childId) {
return new ChildDTO(parent, childId, "A faked child");
}
}
To continue about the warning, requests are declaring the parameter "@ModelAttribute ParentDTO parent": the name of the model attribute is not the variable name ("parent"), nor the original "parentId", but the classname with first letter lowerified: "parentDTO", so we have to be careful to use model.addAttribute("parentDTO"...)
Edit: a simpler, less-error-prone, and more complete example:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
public @interface ProjectDependantRestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
*
* @return the suggested component name, if any
*/
String value() default "";
}
@ControllerAdvice(annotations = ParentDependantRestController.class)
public class ParentInjector {
@ModelAttribute
public ParentDTO injectParent(@PathVariable long parentId) {
return new ParentDTO(parentId, "A faked parent");
}
}
@ParentDependantRestController
@RequestMapping("/api/parents/{parentId:[0-9]+}/childs")
public class ChildResource {
@GetMapping("/{childId:[0-9]+}")
public ChildDTO getOne(@ModelAttribute ParentDTO parent, long childId) {
return new ChildDTO(parent, childId, "A faked child");
}
}