Typically the spring controller method has such view:
#RequestMapping("/url")
public String getUrl([params]){
// do some stuff..
}
As params spring can accept get/post/delete .. params from request or some standart params like model, locale etc.
My question is next: can spring accept my parametr when method is invoked. like:
#RequestMapping("/profile")
public String getUserProfile(UserEntity user, [params ..]){
// do some stuff..
}
Here UserEntity user is NOT a request body, but the entity from my DB.
Scenario is next: before executing method getUserProfile, spring execute some other method, that somehow get user from DB and pass it as method param.
Is there some way to repeat this scenario in real life?
This is possible if you use Spring Data. See the documentation: http://docs.spring.io/spring-data/commons/docs/current/reference/html/#core.web
Basically, what you have to do, is to add the id as parameter, and Spring Data will then use that ID to get the entity from the database and pass it to the method:
#RequestMapping("/{id}")
public String showUserForm(#PathVariable("id") User user) {}
To enable that feature, you have to put the annotation #EnableSpringDataWebSupport onto a #Configuration class
Check here.
We can use MVC interceptors. You need to modify the Interceptor implementation. Check the request URL pattern, if it matches then call a method that returns the UserProfile object. Hope this helps.
Related
I have a problem with data flow in my app..
In the controller I am taking some model from DB, then I pass it to view - here some fields are shown (not all of them), and the user is able to modify them..
Then when he submits form, the controller should update the model in db.
The problem is flow, because not all of the fields are in tags, so they won't pass after submiting the form..
The only solution I found, is to create additional tags with all of the fields, which are not used in to pass them forward..
But in case I have many fields, for example - 30, I would have to create a lot of hidden fields...
What solution do you think would be the best?
Greetings,
M.
You have 2 options
Create a #ModelAttribute annotated method to get the model object from the database for each request
Put it in the session using #SessionAttributes.
#ModelAttribute annotated method
Instead of having a GET method filling the model you can also use a #ModelAttribute annotated method. This method will be invoked before each request handling method in the controller. One thing to take care of is that the id is passed in each request.
#ModelAttribute
public YourObject modelObject(#RequestParam long id) {
return yourObjectDao.findOne(id);
}
Now you can simply remove the filling of the model from the GET method and add a #ModelAttribute annotated method argument to your POST method. Which will then use the freshly obtain object.
Drawback of this approach is that when using optimistic locking it doesn't work so well anymore because each time you get the most recent version.
Using #SessionAttributes
Annotate your controller with #SessionAttributes this instructs the web handling to store the matching model objects in the session and retrieve them from there before binding.
#SessionAttributes("yourObject")
#Controller
public class YourController { ... }
Now in your POST method add an argument of the type SessionStatus and when everything is well call the isComplete method on that object. This will cleanup any session attributes put in the session by this controller.
public String handlePost(#ModelAttribute YourObject model, BindingResult result, SessionStatus status) {
if (result.hasErrors) {
return "yourView";
} else {
status.isComplete();
yourObjectDao.save(model);
return "redirect:your-new-view";
}
}
I'm using spring security and I've 2 methods in my MovieService-
#PreAuthorize("hasRole('ROLE_DIRECTOR')")
public Map<String, Movie> getAllMovies() {
.......
.......
}
And another method is -
public Movie getMovieByMovieCode(String movieCode) {
Map<String, Movie> movies = getAllMovies();
Movie movie = movies.get(movieCode);
return movie;
}
As shown in code, I'm calling getAllMovies() method from inside the getMovieByMovieCode() method. So, if a user who does NOT have ROLE_DIRECTOR role, it tries to access getMovieByMovieCode() method and from inside this method, getAllMovies() method is also accessed.
But the expected behavior is that this user should not be able to access getAllMovies() method as this user does not have role ROLE_DIRECTOR.
How can I achieve this behavior? Do I need to use <dispatcher> inside filter mapping?
Spring Security authorization using annotations is implemented using Spring AOP proxies. That, among other things, means that whenever you are calling methods on the current object, you are referencing the object itself, not the proxy. That is why authentication is not done. The simple workaround would be to annotate the getMovieByMovieCode with the #PreAuthorize annotation too.
Here is some helpfull reading on AOP proxies: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies
I am working on a wizard-like set of pages, where the user has to enter bits of data in several views for a final submission, allowing them to go back-and-forth prior to the final Submit is done. I was trying to use the same Bean, defined as a ModelAttribute, for all the views, basically just passing this one Bean around like a token in which each view adds its little bit of data.
The problem is that Spring MVC seems to create a new Bean on ever call. My admittedly fuzzy understanding about the Model was that it was basically like putting something into session, and that object would be around until the session was done. This does not seem to be the case.
So, I guess the first question is...where do Model Attributes "live", and for how long? Is there a better pattern out there for implementing a wizard-like interface using just Spring MVC (I am limited and can't use Web Flow...its not an approved tool where I work)?
It is NOT a good practise to use Model Attribute as a bean. It is good for manimulating form data before they are persisted into database.
#ModelAttribute("formAttribute") is created when you have specified it in your method as parameter:
public void getForm(#ModelAttribute("formAttribute") Form form) {
}
It is created before every method call by calling its contruct:
#ModelAttribute("formAttribute")
public Form getForm() {
return new Form();
}
When it is not specified in method parameter it doesn't exist.
There is possible to add your #ModelAttribute into session by defining #SessionAttributes on your controller:
#Controller
#SessionAttributes("formAttribute")
public HelloController
Then it is initialized once, when you firstly use it, and destroyed when you destroy it by calling:
public void finalStep(SessionStatus status) {
status.setComplete();
}
I think with combination of #SessionAttributes it is possible in relatively easy way create the wizard-like flow.
If Web-flow is not an option, you can try doing this:
Store your model attribute as a session attribute, this is accomplished by adding a #SessionAttribute annotation to your controller:
#Controller
#SessionAttribute("myconversationModel")
public class MyFlowController{
#RequestMapping
public String myMethod(#ModelAttribute("myconversationModel") ConversationModel myConversationModel){
....
}
}
Then where you think you are done with the flow, just accept an additional parameter SessionStatus and call sessionStatus.complete, this will wipe the attribute from session
#RequestMapping
public String myFinalMethod(#ModelAttribute("myconversationModel") ConversationModel myConversationModel, SessionStatus sessionStatus){
sessionStatus.complete();
....
}
I have a library method Common.addTheUsualStuffToTheModel(model) that needs to add various attributes to the model in every controller method in my app.
#RequestMapping(value = "/everypath", method = RequestMethod.GET)
public final String everyHandler(ModelMap model)
{
model = Common.addTheUsualStuffToTheModel(model);
return "everyPage";
}
So far I have been adding this same line to every handler method:
model = Common.addTheUsualStuffToTheModel(model);
But I'm afraid this is not consistent with the principle of "write once, use everywhere".
How do I avoid repeating this code in every handler?
You can use an interceptor and <mvc:interceptors> to do that
In your interceptor you can add anything as request attribute (which is in fact where the model attributes go). The interceptor code is executed before or after each method (that matches the interceptor mapping).
If you don't necessarily need the model to be populated before the controller method, in the postHandle method you get the ModelAndView object.
What about specifying #ModelAttribute annotated reference data provider methods. If you had a base class for all of your controllers, and that base class had #ModelAttribute annotated methods then I believe that data would be available in the model for all views handled by those controllers. Have a look at 15.3.2.8 in the Spring docs.
I am using Spring MVC and in my controller, I want to be able to automatically bind incoming parameters to my Java object. It seems like this should be pretty easy to do. The only wrinkle is that the incoming parameter names (e.g. "username") may not match up exactly with the field name in the java object (e.g. "name").
From the Spring documentation (http://static.springsource.org/spring/docs/2.5.6/reference/mvc.html):
"Spring Web MVC allows you to use any
object as a command or form object....
All this means that you don't need to
duplicate your business objects'
properties as simple, untyped strings
in your form objects just to be able
to handle invalid submissions, or to
convert the Strings properly. Instead,
it is often preferable to bind
directly to your business objects. "
How do I actually do this? Any code or links appreciated.
For example, my business object
public class User {
private String username;
private String password;
//getters and setter
}
The request my controller is handling:
example.com/login?name=Steve&pw=1234
I would like to bind "Steve" to User.username and "1234" to User.password.
Thanks.
If I remember correctly, you can override public Object formBackingObject(HttpServletRequest request) and manually setup your command POJO.
In this case you can use custom argument resolver.
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
public Object resolveArgument(final MethodParameter parameter,
final ModelAndViewContainer container,
final NativeWebRequest webRequest,
final WebDataBinderFactory binderFactory) {
final User user = new User();
user.setUsername(webRequest.getParameter("name"));
user.setPassword(webRequest.getParameter("pw"));
return user;
}
}
And in your spring config:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="your.package.UserArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
example.com/login?username=Steven&password=1234
You can't expect spring MVC to do automatic binding when the property names don't match up. That's just not logical.
EDIT: Do what Kaleb said, overriding formBackingObject is cleaner.
I'll leave this note though:
However, if you have any control over both sides, I strongly recommend making the name consistent. Convention over configuration.
If you want to bind request to the POJO you may need to extend AbstractCommandController or if you have a form - SimpleFormController. I believe you will need the servlet version from org.springframework.web.servlet.mvc package.
use setCommandClass() to setup the correct type of your backing bean in controller constructor, ie POJO:
setCommandClass(User.class);
formBackingBean() used to create new instance of the POJO, that would be used by controller.
controller is responsible for mapping request parameters to the POJO fields.
if mapping is not working good for you, than override formBackingBean() or onBind() methods to read request paratemers and put values to POJO.
You should change your form or your object so that the names match up. If you're trying to use the features in a framework like Spring MVC, you should probably follow their conventions.
Convention over configuration.