So I've found a bunch of Struts 2 CRUD examples around the web:
Struts 2 CRUD Demo
and a few books:
Apache Struts 2 Web Application Development ISBN: 978-1847193391
Struts 2 Design and Programming ISBN: 978-0980331608
But all of them differ a little bit on how to do form population.
Some suggest implementing the Java interfaces ModelDriven or Prepareable to call come prepare function to pre-populate any needed data members.
Others suggest creating your own PrepareForUpdate action that calls a pre-populate function then redirects to the main edit view.
They also very on how to pass around an object identifier to indicate what object to retrieve for editing. SOme suggest intercepters what others throw it in the URL parameters and retrieve it through ActionContext or pass it around through a s:hidden field.
Is there a Best Practices way to do form population in Struts 2?
What are the advantages/disadvantages to the methods mentioned above?
I'm not aware of any documented best practices, but I've been using Webwork and Struts2 for about three years now, so I can tell you what I've used in my projects. By the way, the CRUD demo documentation you linked to strikes me as a bit out of date (I realize its from the project site).
I split my CRUD work into three different actions:
An action that lists the entities. It supports pagination and populates some type of a table or grid view.
An action that handles both add and edit functionality. Uses a prepare() method to set up dropdowns, etc.
An action that handles delete functionality.
Some suggest implementing the Java interfaces ModelDriven or Prepareable to call come prepare function to pre-populate any needed data members.
That's the approach that I would advocate, although I don't use the ModelDriven interface. For details, check out how Struts2 ModelDriven interface works and the comments on my answer. Whether you use ModelDriven or not is just a personal choice. Also, check out why is model-driven action preferred over object backed bean properties.
Others suggest creating your own PrepareForUpdate action that calls a pre-populate function then redirects to the main edit view.
I have not seen that before and based on your description, I would avoid that technique. It seems wasteful to do a redirect and create an additional HTTP request to achieve the same thing that the prepare() method was designed to handle.
They also very on how to pass around an object identifier to indicate what object to retrieve for editing.
Just pass the identifier in the URL or the form. That's the standard approach for web applications.
I've been using Struts 2 for about 3 years. I use ModelDriven and Prepareable together in the same action. Each domain object (model) has a struts action class that returns a list or single object depending on if the id was passed to the action. This works pretty well for me, and the only time it's been problematic is when using Ajax. I usually separate my Ajax actions into a separate action for the model, if I am using them. I store the model id, as well as any related objects that I might need as hidden HTML fields in the view.
Using this approach, the action and the view are restful. You can leave the page for a long period of time and invoke the action without fear that the action will fail. Here's an example:
public class ApplicationAction extends MyBaseAction
implements ModelDriven<Application>, Preparable {
private static final long serialVersionUID = 7242685178906659449L;
private ApplicationService applicationService;
private Application application;
private Integer id;
List<Application> allApplications;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Application getModel() {
return application;
}
public void prepare() throws Exception {
if(id == null || id.intValue() == 0){
application= new Application();
}else{
application= applicationService.getApplication(id);
}
}
#SkipValidation
public String list() throws Exception {
allApplications = applicationService.getApplications();
return SUCCESS;
}
#Validations( visitorFields = {#VisitorFieldValidator(message = "Validation Error", fieldName = "model", appendPrefix = false)})
public String update() throws Exception {
applicationService.saveApplication(application);
addActionMessage("Application Saved Successfully.");
return SUCCESS;
}
public void setApplicationService(ApplicationService applicationService) {
this.applicationService = applicationService;
}
public List<Application> getAllApplications() {
return allApplications;
}
}
Related
I am trying to write a custom validator in a Spring MVC application. I would like to know if there a way to pass the #Model object to a custom spring validator?
Say, I have a Person object and Account object. I have to write a custom validator to validate Person, but the validation is dependent of the Account object and other objects in session.
For example, Person cannot have more than 3 accounts, account types have to be of specific category and not old than 3 years (this value, ie the number years is dynamic based on the profile logged in and is in session).
How can I pass both objects, especially #Model to the validator.
public class ServiceValidator implements Validator {
#Autowired
private ServiceDAO servicesDao;
#Override
public boolean supports(Class<?> clasz) {
return clasz.isAssignableFrom(Person.class);
}
#Override
public void validate(Object obj, Errors errors) {
Person subscriber = (Person) obj;
// How can I access #Model object here ???
}
Doubt if you can but have two workarounds:
a. If it is persisted data that you are looking for, probably it is just better to retrieve it once more in the validator and validate using that data, so for eg, in your case if you are validating person and persons account details are retrievable from DB, then get it from DB and validate in your validator using the retrieved data.
b. Probably this is a better approach if the number of places where you need to use the validator is fairly confined:
public class ServiceValidator {
#Autowired
private ServiceDAO servicesDao;
public void validate(Person subscriber, List<Account> accounts, ..., Errors errors) {
}
Just call the above validator directly from your requestmapped methods..
In your controller..
List<Account> accounts = //retrieve from session
serviceValidator.validate(subscriber, accounts, ...errors);
if (errors.hasErrors())..
else..
The application I am working on is multi-user and multi-company and I am having trouble at the moment trying to figure out the most efficient/best way to ensure data level security, in broad terms prevent UserA from seeing UserB's data. If there are various controllers (Products, Orders, etc) and models, then the routes are something like Product/Edit/1 and Order/Edit/1. However, to ensure that users cannot alter the routes to see each others data it seems that each service layer/db layer call will require me checking that the specific product key/order key belongs to the authenticated user? Is this the best option or am I missing something more elegant.
Edit Update
From Omri's answer below, the first link actually has a link to here. It mentions the various ways to accomplish the access level security, but I guess this is what I want to know people's opinions about. Should I do something like this:
public class ProductController
{
public ActionResult Edit(int id)
{
if (_productService.CanUserEdit(id, userID))
{
_productService.Save(id);
}
else
{
throw UnauthorizedException;
}
return RedirectToAction("Index");
}
}
OR
public class ProductController
{
public ActionResult Edit(int id)
{
_productService.Save(id, userID);
return RedirectToAction("Index");
}
}
public class ProductService
{
public void Save(int id, int userID)
{
if (CanUserEdit(id, userID))
{
//DO SAVE
}
}
private CanUserEdit(int id, int userID)
{
}
}
Obviously there is not much difference between the two implementations, just whether or not the action takes place within the Controller or at the service level. The service level changes on the fly based on the company, so my guess is that we probably should do the first option and have the product service for each company derive from a common base class that implements the CanUserEdit capability since that does not change.
Seems to be two common approaches: OnActionExecuting or AuthorizeAttribute. See here:
How to Extend/Architect the ASP.NET MVC 3 Authorize Attribute to Handle This Scenario
ASP.NET MVC 3 also has Global Action Filters which allow you to apply action filters globally without the need for explicit attribute declaration:
http://blog.tallan.com/2011/02/04/global-action-filters-in-asp-net-mvc-3/
What's a good way to validate a model when information external to the model is required in order for the validation to take place? For example, consider the following model:
public class Rating {
public string Comment { get; set; }
public int RatingLevel { get; set; }
}
The system administrator can then set the RatingLevels for which a comment is required. These settings are available through a settings service.
So, in order to fully validate the model I need information external to it, in this case the settings service.
I've considered the following so far:
Inject the service into the model. The DefaultModelBinder uses System.Activator to create the object so it doesn't go through the normal dependency resolver and I can't inject the service into the model without creating a new model binder (besides which, that doesn't feel like the correct way to go about it).
Inject the service into an annotation. I'm not yet sure this is possible but will investigate further soon. It still feels clumsy.
Use a custom model binder. Apparently I can implement OnPropertyValidating to do custom property validation. This seems the most preferable so far though I'm not yet sure how to do it.
Which method, above or not, is best suited to this type of validation problem?
Option 1 doesn't fit. The only way it would work would be to pull in the dependency via the service locator anti-pattern.
Option 2 doesn't work. Although I couldn't see how this was possible because of the C# attribute requirements, it is possible. See the following for references:
Resolving IoC Container Services for Validation Attributes in ASP.NET MVC
NInjectDataAnnotationsModelValidatorProvider
Option 3: I didn't know about this earlier, but what appears to be a very powerful way to write validators is to use the ModelValidator class and a corresponding ModelValidatorProvider.
First, you create your custom ModelValidatorProvider:
public class CustomModelValidatorProvider : ModelValidatorProvider
{
public CustomModelValidatorProvider(/* Your dependencies */) {}
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
if (metadata.ModelType == typeof(YourModel))
{
yield return new YourModelValidator(...);
}
}
}
ASP.NET MVC's IDependencyResolver will attempt to resolve the above provider, so as long as it's registered with your IoC container you won't need to do anything else. And then the ModelValidator:
public class EntryRatingViewModelValidatorMvcAdapter : ModelValidator
{
public EntryRatingViewModelValidatorMvcAdapter(
ModelMetadata argMetadata,
ControllerContext argContext)
: base(argMetadata, argContext)
{
_validator = validator;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (/* error condition */)
{
yield return new ModelValidationResult
{
MemberName = "Model.Member",
Message = "Rating is required."
};
}
}
}
As the provider is retrieved through the IDependencyResolver and the provider has full control over the returned ModelValidators I was easily able to inject the dependencies and perform necessary validation.
You could try fluent validation. It supports asp.net mvc and DI so you can inject external services into your validators.
Assuming that you want both client and server-side validation of the model based upon the values returned from the service, I would opt for 2., Inject the service into an annotation.
I give some sample code in my response to this question about adding validators to a model. The only additional step in your case is that you will need to inject your service into your class inheriting from DataAnnotationsModelValidatorProvider.
What about just simply using IValidateableObject and in that method determine if validation is appropriate or not and setting the errors there?
How do I use IValidatableObject?
Learning ASP.NET MVC with a new project, and a little unsure of where some things should happen. I've read that ViewModels are a Good Thing (tm) and had planned on doing it in a similar fashion anyway, but I'm still not entirely clear on the responsibilities of the Model vs. the Controller.
Should the ViewModel be responsible for actually loading itself from the ORM? Thus the controller would just call ViewModel.GetObject() and pass the result back to the view?
Or should I load the data in the Controller, and then transform it into the ViewModel? Seems like that puts a lot of work in the Controller though, which is supposed to be kept somewhat lightweight.
I guess I could also have a third party that is responsible for pulling the data, then the Controller would call that and transform it for the appropriate ViewModel.
So any thoughts on what is the "best" approach?
The controller will create the viewmodel object and fill it out using the model. The model should use the ORM to get the data.
The ViewModel is always specific to the view only, and the model is specific to the domain. In CQRS you would actually just get the ViewModel and send it to the view.
From the controller you can do what ever it takes to make your CRUD happen for the view. If you use the Repo pattern thats ok, if you use NHibernate or EF directly thats cool tool. Once the ViewModel goes to the view it will be disconnected from everything like the DB, so fill it out before it gets there.
Personally I use a repository. So the controller queries a repository and gets a model, then maps the model to a view model and passes the view model to the view. Example:
public class ProductsController: Controller
{
private readonly IProductsRepository _repository;
private readonly IMapperEngine _mapper;
public ProductsController(IProductsRepository repository, IMapperEngine mapper)
{
_repository = repository;
_mapper = mapper;
}
public ActionResult Index(int id)
{
Product product = _repository.GetProduct(id);
ProductViewModel viewModel = _mapper.Map<Product, ProductViewModel>(product);
return View(viewModel);
}
}
And because this is repetitive logic I use custom action filters:
public class ProductsController: Controller
{
private readonly IProductsRepository _repository;
public ProductsController(IProductsRepository repository)
{
_repository = repository;
}
[AutoMap(typeof(Product), typeof(ProductViewModel))]
public ActionResult Index(int id)
{
Product product = _repository.GetProduct(id);
return View(product);
}
}
in this case the custom action filter intercepts the result of the action and replaces it using the corresponding mapping layer.
The way this repository is implemented is not the responsibility of the controller (whether it is an ORM, direct SQL queries, or even distant web service calls). As long as it is injected some proper implementation it will work which allows for weaker coupling between the different parts of the application and easier unit testing in isolation. So in this example it is the implementation of the repository that is responsible for fetching data.
I'm working with Spring MVC and I'd like it to bind a a persistent object from the database, but I cannot figure out how I can set my code to make a call to the DB before binding. For example, I'm trying to update a "BenefitType" object to the database, however, I want it to get the object fromthe database, not create a new one so I do not have to update all the fields.
#RequestMapping("/save")
public String save(#ModelAttribute("item") BenefitType benefitType, BindingResult result)
{
...check for errors
...save, etc.
}
There are several options:
In the simpliest case when your object has only simple properties you can bind all its properties to the form fields (hidden if necessary), and get a fully bound object after submit. Complex properties also can be bound to the form fields using PropertyEditors.
You may also use session to store your object between GET and POST requests. Spring 3 faciliates this approach with #SessionAttributes annotation (from the Petclinic sample):
#Controller
#RequestMapping("/owners/*/pets/{petId}/edit")
#SessionAttributes("pet") // Specify attributes to be stored in the session
public class EditPetForm {
...
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
// Disallow binding of sensitive fields - user can't override
// values from the session
dataBinder.setDisallowedFields("id");
}
#RequestMapping(method = RequestMethod.GET)
public String setupForm(#PathVariable("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet); // Put attribute into session
return "pets/form";
}
#RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
public String processSubmit(#ModelAttribute("pet") Pet pet,
BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "pets/form";
} else {
this.clinic.storePet(pet);
// Clean the session attribute after successful submit
status.setComplete();
return "redirect:/owners/" + pet.getOwner().getId();
}
}
}
However this approach may cause problems if several instances of the form are open simultaneously in the same session.
So, the most reliable approach for the complex cases is to create a separate object for storing form fields and merge changes from that object into persistent object manually.
So I ended up resolving this by annotating a method with a #ModelAttribute of the same name in the class. Spring builds the model first before executing the request mapping:
#ModelAttribute("item")
BenefitType getBenefitType(#RequestParam("id") String id) {
// return benefit type
}
While it is possible that your domain model is so simple that you can bind UI objects directly to data model objects, it is more likely that this is not so, in which case I would highly recommend you design a class specifically for form binding, then translate between it and domain objects in your controller.
I'm a little confused. I think you're actually talking about an update workflow?
You need two #RequestMappings, one for GET and one for POST:
#RequestMapping(value="/update/{id}", method=RequestMethod.GET)
public String getSave(ModelMap model, #PathVariable Long id)
{
model.putAttribute("item", benefitDao.findById(id));
return "view";
}
then on the POST actually update the field.
In you example above, your #ModelAttribute should already be populated with a method like the above method, and the properties be bound using something like JSTL or Spring tabglibs in conjunction with the form backing object.
You may also want to look at InitBinder depending on your use case.