Add a Model Attribute using ControllerAdvice - spring

I have marked a class with #ControllerAdvice
Added a method
#ModelAttribute
public void setSourceAppId(Model model)
{
model.addAttribute("myattribute","1234");
}
But this method is not getting called at all.

I have not yet used modelattribute with a setter method so i cannot tell that thats a wrong approach, but changing it to a getter will do the job. Also, you dont need to add it to the model yourself. Modelattribute is handling exactly that.
#ModelAttribute("myattribute")
public int getSourceAppId()
{
return 1234;
}

Related

Add attribute to wildcard Requestmapping path?

Is there a way to add an attribute to all paths of a certain user?
I.e I am trying to reach the current logged in administrator on all pages the administrator can reach, but I don't want to add this attribute to every single controller.
Something like this, where I don't need to return anything:
#RequestMapping(value = {"admin/**"}, method = RequestMethod.GET)
public void adminPaths(ModelMap model) {
model.addAttribute("user", getPrincipal());
}
You can use #ModelAttributes on a method in a controller. An #ModelAttribute on a method indicates the purpose of that method is to add one or more model attributes to all controller methods:
#Controller
#RequestMapping("/admin")
public class AdminController {
...
#ModelAttribute
public void populateModel(Model model) {
model.addAttribute("user", getPrincipal());
// add more ...
}
...
}
#ModelAttribute methods in a controller are invoked before #RequestMapping methods, within the same controller.
For truly wildcard matching, you can use ControllerAdvice and ModelAttributes on methods together. Something like following:
#ControllerAdvice(annotations = Controller.class)
public class AdminPopulatorAdvice {
#ModelAttribute
public void populateModel(HttpServletRequest request, Model model) {
// examine the request
// if its path contains /admin, then add attribute
model.addAttribute("user", getPrincipal());
// add more ...
}
}
I forgot to mention that I was using Spring security.
Bohuslav pointed me into the right direction and ended up here: https://docs.spring.io/spring-security/site/docs/current/reference/html/taglibs.html

Spring model attribute declaration

What is the difference between declaring #ModelAttribute as a method parameter and using model.asMap()?
Example:
public void method1(#ModelAttribute("attr") MyObject myObj) {
...
}
public void method2(Model model) {
MyObject myObj = (MyObject) model.asMap().get("attr");
}
These are not the same if that's what your thinking. I tried to get an attribute from the model doing it the first way but didn't work so ended up doing it the second way.
Thanks
EDIT: What I mean by "it didn't work" is that when I used #ModelAttribute, it was confusing the values of the variables in that object to another object of a different type which had the same variable names.
EDIT #2: No it is not a duplicate because he does not address the model.asMap() method. As per the other post, I tried using #ModelAttribute on a #SessionAttributes variable but ran into problems so had to use model.asMap()

WebApi action parameters validation by ValidationAttribute

Does WebAPI can handle ValidationAttribute on action parameter?
For instance:
public class SampleController : ApiController
{
public string Get([RegularExpression("sampleExpression")]string id)
{
return "result";
}
}
In this sample WebAPI doesn't invoke any methods of RegularExpressionAttribute (or any other data annotation attribute) to validate input parameter. But in case if we passing an object as parameter, for instance a class then WebAPI can validate properties.
Is it a bug? Or I'm doing something wrong?
UPD: It's an open issue:
http://aspnetwebstack.codeplex.com/workitem/24?PendingVoteId=24
Does anyone can suggest a workaround?
This is a genuine question, I'm curious why not just do something like :
public class SampleController : ApiController
{
public string Get(string id)
{
RegularExpressionHelper.ValidateWith("sampleExpression",id);
return "result";
}
}
To me this seems to be equivalently concise. It is obvious when the validation occurs. I can safely assume that if the validation fails then an exception will be thrown. I can easily add additional validation and be confident of the order in which they occur.
Regarding the attribute method, I don't know if the validation is used as part of the route matching, I have no idea what happens if it fails. I don't know what filters run before or after the validation. I'm sure if I knew MVC better I would know the answer to these questions, but I don't see the advantage of using an attribute that makes my code's behaviour dependent on some framework controlled infrastructure.
Am I missing some significant benefit?
I had the same doubt. My workaround consists in creating a class just for encapsulating the parameter, so I can decorate it with the validation attribute I want. I could use the workaround proposed by Darrel in his answer, but I have a filter that checks if ModelState.IsValid before entering the action, so I need to validate before the action gets executed.
[ModelBinder]
public class Item
{
[RegularExpression("sampleExpression")]
public string Id { get; set; }
}
The class must be annotated with [ModelBinder], otherwise the parameter binding mechanism will try to extract the id field from the body of the request. Read this article for more info.
Also, note that Id is now in PascalCase instead of camelCase. Read this article to understand how the conversion is made.
The action signature is:
public string Get(Item item)

How model annotated methods should interact?

I would like to know how controller methods should interact with ModelAttribute annotated methods.
For example handlePage method would like to filter the list created by createList method?
Or set the id for the object created by createAnObject method?
Is it possible or ModelAttribute annotated methods are designed to attach static data to the model?
#ModelAttribute("someList")
public ArrayList<SomeList> createList() {
return new ArrayList<SomeList>(100);
}
#ModelAttribute("anObject")
public AnObject createAnObject() {
return new MyObject();
}
#RequestMapping(method=RequestMethod.GET)
public void handlePage(Model model) {
//Do some stuff to populate the model....
}
The two shouldn't really interact. #ModelAttribute, in this context, is intended for exposure of reference data, i.e. data that doesn't depend on the details of the request.
If your handler method needs to modify that data, then #ModelAttribute isn't appropriate. Instead, the handler method should explicitly add the data to the model after modifying it.

AOP for Spring Controllers

Spring's AOP functionality is pretty great, and it makes it easy to add cool and useful annotations to controllers. For example, I wrote an #Authenticated annotation that either allows authenticated users through to the controller method or redirects to the login page. Fun stuff.
However, Spring's controllers can return all sorts of different types. They can return Strings, ModelAndView objects, or even void. There are methods in my code base that use all three types. However, I'd like to change my #Authenticated annotation to render and return a particular page, which I was hoping to do by returning a ModelAndView object. Is the only way to accomplish this by requiring all of my controller methods to return a ModelAndView?
Example of a controller I'd like to have:
#Controller
public class MyController() {
#Authenticated
#RequestMapping("/myscore")
public String myScorePage(ModelMap model) {
return "myScorePage";
}
#Authenticated
#RequestMapping("/anotherPage")
public ModelAndView something() {
return new ModelAndView("anotherPage",someModelStuff());
}
}
#Aspect
public class NotVeryUsefulAspect {
#Around("#annotation(Authenticate)")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
if( isAuthenticated() ) {
return pjp.proceed();
} else {
return /* Oh no what goes here, I want to render a FAILURE page without redirecting */
}
}
}
Ha, figured it out!
I decided to use the ProceedingJoinPoint passed to the aspect method to figure out the return type of the original method. Then I made a set of possible "failure" results for the aspect method based on what type of return is passed. For example, if the method originally returned a String, I return "failure_page", and if the method returned a ModelAndView, I return a new ModelAndView("failure_page").
Works quite well! Unfortunately, I may not have an opportunity to set a model object if it returns a string and doesn't take a ModelMap as a parameter, but I can deal with that for an error page just fine.
Yes it seams that you are right.
You need to change your methods so that all return an ModelAndView.
Or you need two Aspects, one for return type ModelAndView and one for String - and then all your controller methods must match
But Authorization is already build in in Spring Security and you do not need to implement it by your own.

Resources