Usually i have seen a server side validation in spring mvc like this for example there is customer page which contains customer related information like customerName ,customerAddress,customerPhoneNumber for this we going to have
customer model object then in the spring controller we are going to call like this
Spring Controller
#RequestMapping(value = "/customerRegistrationScreen")
public String customerRegistrationScreen(Model model) {
Customer customer= new Customer();
model.addAttribute("customer", customer);
return "customerRegistrationScreen";
}
#RequestMapping(value = "/doCustomerRegistration", method = RequestMethod.POST)
public ModelAndView registerCustomer(#ModelAttribute("customer") #Validated Customer customer, BindingResult result,Model model) {
if (result.hasErrors()) {
} else {
}
}
CustomerValidator class
#Component
public class CustomerValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Customer.class.equals(clazz);
}
#Override
public void validate(Object obj, Errors err) {
ValidationUtils.rejectIfEmpty(err, "name", "customer.name.empty");
ValidationUtils.rejectIfEmpty(err, "email", "customer.email.empty");
ValidationUtils.rejectIfEmpty(err, "gender", "customer.gender.empty");
ValidationUtils.rejectIfEmpty(err, "languages", "customer.languages.empty");
User user = (User) obj;
Pattern pattern = Pattern.compile("^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$",
Pattern.CASE_INSENSITIVE);
if (!(pattern.matcher(customer.getEmail()).matches())) {
err.rejectValue("email", "user.email.invalid");
}
}
}
customerRegistration.jsp
<form:form method="post" modelAttribute="customer" action="doCustomerRegistration">
</form:form>
what if the jsp have two model object information like Customer and product information like customerName,customerAddress,customerPhoneNumber,productID,productName,productPrice here i like to have two model object like customer
& Product if i have two model object how can i map model attribute from jsp and Spring contoller and how can i do server side validation for both the validation
Although it is a good idea to keep models separate in each form, but for this particular use case it can be achieved by following below steps.
The best way to achieve this is to wrap both the Model attributes in one wrapper class and use it in validation.
Lets say Product class looks like this.
public class Product{
String productName;
// other fields and their getter setters
}
Create a wrapper class which wraps both models Customer and Product
public class CustomerProductWrapper {
private Customer customer;
private Product product;
//getter setter
}
In your validator class , change the implementation of supports() method as below
#Override
public boolean supports(Class clazz) {
return CustomerProductWrapper .class.equals(clazz);
}
2.1 Change the implementation of Validation Method as below
#Override
public void validate(Object obj, Errors err) {
//The object that you get now is CustomerProductWrapper object
// refer the fields using this object
ValidationUtils.rejectIfEmpty(err, "customer.name", "customer.name.empty");
ValidationUtils.rejectIfEmpty(err, "customer.email", "customer.email.empty");
ValidationUtils.rejectIfEmpty(err, "customer.gender", "customer.gender.empty");
ValidationUtils.rejectIfEmpty(err, "customer.languages", "customer.languages.empty");
CustomerProductWrapper cpw= (CustomerProductWrapper ) obj;
Pattern pattern = Pattern.compile("^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$",
Pattern.CASE_INSENSITIVE);
if (!(pattern.matcher(cpw.getCustomer().getEmail()).matches())) {
err.rejectValue("customer.email", "user.email.invalid");
}
//validate a Product field
ValidationUtils.rejectIfEmpty(err, "product.productName", "product.name.empty");
}
In your controller mapping ,
public String customerRegistrationScreen(Model model) {
CustomerProductWrapper cpw= new CustomerProductWrapper ();
model.addAttribute("cpw", cpw);
return "customerRegistrationScreen";
}
And
#RequestMapping(value = "/doCustomerRegistration", method = RequestMethod.POST)
public ModelAndView registerCustomer(#ModelAttribute("cpw") #Validated CustomerProductWrapper cpw, BindingResult result,Model model) {
if (result.hasErrors()) {
} else {
}
}
And finally in your view page
<form:form method="post" modelAttribute="cpw" action="doCustomerRegistration">
</form:form>
Also refer fields using cpw's attributes that is
<form:input path="name" />
<form:errors path="name" cssClass="error" />
will change to
<form:input path="customer.name" />
<form:errors path="customer.name" cssClass="error" />
Similarly for product validation you can use
<form:input path="product.productName" />
<form:errors path="product.productName" cssClass="error" />
That's all.
Related
I have a jsp form with an input box, a domain object with get/set methods, and a controller. When I submit the form I get null values in the controller. The "set" method is never being called in the domain object when i submit the form but the object itself is being called.
Order.jsp
<portlet:defineObjects />
<portlet:actionURL portletMode="view" var="createNewOrderURL">
<portlet:param name="action" value="createNewOrder" />
</portlet:actionURL>
<div>
<form:form name="form" method="post" commandName="refOrder" action="${createNewOrderURL}" id="createOrder">
TestName : ${refOrder.name}<br/> <!-- here I get the correct value to display -->
<form:input path="referenceNum" />
<input type="submit" value="Submit" />
</form:form>
</div>
Order.java
public class Order {
private String name = "Name1";
private String referenceNum;
public Order(){
System.out.println("Inside Order.java");
System.out.println(getReferenceNum());
}
public Order(String name, String referenceNum) {
this.name = name;
this.referenceNum = referenceNum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getReferenceNum() {
return referenceNum;
}
public void setReferenceNum(String referenceNum) {
this.referenceNum = referenceNum;
}
SalesOrderController.java
#RenderMapping
public String defaultRender(RenderRequest request, RenderResponse response, Model model) throws SQLException, NamingException{
model.addAttribute("refOrder",new Order());
return "SalesOrderEntry";
}
#ActionMapping(params={"action=createNewOrder"})
public void addNewOrder(ActionRequest request, ActionResponse response, #ModelAttribute("refOrder") Order order)throws NamingException, SQLException{
System.out.println("Inside addNewOrder method");
System.out.println("New Order is --> "+order.toString());
System.out.println("RefNum - "+order.getReferenceNum());
System.out.println("request.getParameter is "+request.getParameter("referenceNum"));
}
I get null for all the print statements in the controller. Have been trying to fix this for two days now and I can't find what's wrong. Would really appreciate if someone can help me get this to work.
Do you have the following in your src/main/webapp/WEB-INF/liferay-portlet.xml descriptor?
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Also, you might want to take a look at the PortletMVC4Spring project, which is the successor to Spring Portlet MVC. The GitHub project contains archetypes that work in Apache Pluto and Liferay Portal. The requires-namespaced-parameters config option is conveniently set in the archetypes.
I am trying to send data from a view to a controller Create method. But the view model parameter is getting null values when the create method is called.
In my view I want to add a item and show a list of added items.
I have tried to send data to the create method but its view model parameter is getting null values.
In the following code whenever Create method is hit the value of p.posts and p.post is null. How can I get the value of p.post and p.posts here?
Controller method
public ActionResult Create(PostsViewModel p) {}
View Model
public class PostsViewModel
{
public IEnumerable<Post> posts;
public Post post;
}
View
#model NotesWebApplication.ViewModels.PostsViewModel
...
#using (Html.BeginForm()) {
...
#Html.EditorFor(model => model.post.postText, new { htmlAttributes = new { #class = "form-control" } })
...
<input type="submit" value="Create" class="btn btn-default" />
Also in my Create method if I wanted to add Bind then which should be added
[Bind(Include="postText")]
or
[Bind(Include="post.postText")]
update
I made the following changes in PostsViewModel class
public class PostsViewModel
{
public IEnumerable<Post> posts { get; set; }
public Post post { get; set; }
}
and the Create method in the controller is changed to
[HttpPost]
public ActionResult Create([Bind(Include="post, posts")]PostsViewModel p) {}
This is what the httpget Create method looks like
// GET: Posts/Create
public ActionResult Create()
{
PostsViewModel postsViewModel = new PostsViewModel();
postsViewModel.posts = db.Posts;
postsViewModel.post = new Post();
return View(postsViewModel);
}
Now when I submit the form p.post in the controller parameter receives the desired value. But p.posts remains null. Why is this hapenning?
I guess the reason is u don`t have an instance of your post object. Try to make your viewModel something like this:
public class PostsViewModel
{
public string PostText {get;set;} // don`t forget to make it like property, not just a field
}
and then create an instance in your controller:
public ActionResult Create(PostsViewModel p)
{
Post post = new Post{ postText = p.PostText};
//and do what you want with it
}
I want to do something very simple, which is to create an HTML button that calls a controller function when clicked, the same as this HTML actionlink. This should really be remarkably easy. The action link is:
#Html.ActionLink("Submit", "Submit", "Home")
I'm using the Razer viewmodel and .NET 4.5. I've done some research, and it seems that I may have to create my own custom button. I'm fine with that, but is that really necessary? See: Mvc Html.ActionButton. It would seem like an oversight for this to have no native microsoft support, but if not, I can live with that.
Please forgive the naivety of this question - I'm new to ASP.NET, though not to C# or web development. Thanks!
I grabbed this from somewhere. but you can map view actions to controller actions with the following code.
Create a class with the following code.
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
public string Name { get; set; }
public string Argument { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
isValidName = true;
}
return isValidName;
}
}
In your View code you can have the following submit buttons
<input type="submit" value="Action A" name="action:ActionA" />
<input type="submit" value="Action B" name="action:ActionB" />
And your controller contains the following code.
[HttpPost]
[MultipleButton(Name="action", Argument="ActionA")]
public ActionResult MyActionA(myModel model)
{
...
}
[HttpPost]
[MultipleButton(Name = "action", Argument = "ActionB")]
public ActionResult MyActionB(myModel model)
{
...
}
Thanks in advance for any help.
I have a form that is being validated with JSR 303. After validation fails, the controller returns the form, shows validation errors, and renders the form with the original values. This works fine with all types of form elements except the mutli-select element.
The command object:
public class TaskOrder implements Serializable {
private static final long serialVersionUID = 1L;
...
#XmlTransient
#ManyToMany
#<OtherJPAAnnotations...>
private List<Contractor> subcontractors;
...
}
Contractor class:
public class Contractor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#<OtherJPAAnnotations...>
private Integer id;
#<OtherJPAAnnotations...>
private String name;
}
Controller:
#RequestMapping(value="processingPath", method=RequestMethod.POST)
public String createNewTaskOrder(#Valid #ModelAttribute TaskOrder taskOrder,
BindingResult result,
Model model) {
...
if (!result.hasErrors()) {
//No binding errors, lots of processing...
else {
model.addAllAttributes(result.getModel());
model.addAttribute(taskOrder);
model.addAttribute("subs", myDAOInstance.getSubs());
return this.setupNewTaskOrder(model);
}
}
#RequestMapping("getFormPath")
public String setupNewTaskOrder(Model model) {
if (!model.containsAttribute("taskOrder")) {
TaskOrder taskOrder = new TaskOrder();
taskOrder.setId(0);
model.addAttribute(taskOrder);
}
return "_n/admin/taskOrder/new";
}
The form:
<form:form commandName="taskOrder" action="processPath">
...
<form:select path="subcontractors">
<form:options items="${subs}" itemValue="id" itemLabel="name"/>
</form:select>
...
</form>
When I open an existing "TaskOrder" with the same form, the values are selected in the "subcontractors" multi-select.
However, trying to add a new "TaskOrder", when it returns from validation the values aren't selected. I know that the selected values are being attached to the model and returned, but just not being selected in the select element.
Thanks for any help!
Thanks for the help. Biju...you were correct!
For those who may stumble across this issue, I added the "EqualsUtil" class described here to my project:
http://www.javapractices.com/topic/TopicAction.do?Id=17
Then added the following method to my Contractor class:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Contractor)) return false;
Contractor c = (Contractor) o;
//Here I ignore the other properties since "id" and "name" are what
//I'm primarily concerned with...
return EqualsUtil.areEqual(this.name, c.name) &&
EqualsUtil.areEqual(this.id, c.id);
}
I submit a form, lets say this form contains
<input name="address" ..>
and
<input name="billingAddress" ..>
i have 2 objects to which i need to bind to:
class Address {
String address;
..
}
class BillingAddress {
String address;
..
}
obviously billingAddress wont bind to address in BillingAddress without some magic.
lets say i have multiple identical fields in both Address and BillingAddress but on the form i prefix the billing inputs with billing, ie billingFirstName, billingLastName etc.
is there some elegant way i can bind to BillingAddress that i can reuse for similar problems?
(or is there a better way to solve this then what i have come up with?)
If you wand to use more than one ModelAttribute, you have to create a wrapper object, which holds an instance of each ModelAttribute. In your case I would create a wrapper object called "FormModel" which holds an instance of Address and an instance of a BillingAddress.
class FormModel {
private Address address;
private BillingAddress billingAddress;
// Getters and Setters
}
Now use FormModel as your ModelAttribute.
In your Form you can define your input-elements like:
<input name="address.address" ..>
<input name="billingAddress.address" ..>
Controller:
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(Model model, #ModelAttribute() FormModel formModel) {
// process formModel.getAddress()
// process formModel.getBillingAddress()
return "redirect:home";
}
If you use custom validators for Address and BillingAddress, you also have to create a FormModelValidator that calls the AddressValidator and BillingAddressValidator:
public class FormModelValidator implements Validator {
private final AddressValidator addressValidator;
private final BillingAddressValidator billingAddressValidator;
public FormModelValidator(AddressValidator addressValidator,
BillingAddressValidator billingAddressValidator) {
this.addressValidator = addressValidator;
this.billingAddressValidator = billingAddressValidator;
}
public boolean supports(Class<?> clazz) {
return FormModel.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
FormModel formModel = (FormModel) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator,
formModel.getAddress(), errors);
} finally {
errors.popNestedPath();
}
try {
errors.pushNestedPath("billingAddress");
ValidationUtils.invokeValidator(this.billingAddressValidator,
formModel.getBillingAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}