Spring MVC application (data binding - custom type converter, data format, data verification)

Keywords: Spring Attribute Java Hibernate

1, Ask questions

When spring MVC encapsulates custom type objects, JavaBean s are bound to the data submitted by the page one by one. Here's what you need to know:

1) all data submitted by the page are strings

2) attributes in JavaBean s, such as Integer age;

When binding data, the following operations are involved:

1) data type conversion during data binding? String--Integer

2) data formatting during data binding? For example, the submitted date is converted: birth = December 15, 2017 -- > date December 15, 2017

3) data verification? The submitted data must be legal, with front-end verification (JS + regular expression), and back-end verification is also required.

2, Data binding

2.1 data binding process

  1. The spring MVC main framework passes the ServletRequest object and the incoming parameter instance of the target method to the WebDataBinderFactory instance to create the DataBinder instance object.
  2. DataBinder calls the ConversionService component assembled in the context of spring MVC to perform data type conversion and data formatting. Populates the request information in the Servlet into the input parameter object.
  3. Call the Validator component to verify the data validity of the input parameter object with the request message bound, and finally generate the data binding result BindingData object.
  4. Spring MVC extracts the input parameter object and validation error object from BindingResult and assigns them to the response input parameter of the processing method.

Spring MVC parses the target processing method through reflection and mechanism, and binds the request message to the input parameter of the processing method. The core component of data binding is DataBinder. The operation mechanism is as follows:

2.2 custom type converter

2.2.1 type converter overview

ConversionService is the core interface of spring type conversion system. You can use the ConversionServiceFactoryBean to top a ConversionService in spring's IOC container. Spring will automatically identify the ConversionService in the IOC container, and use it for data conversion in Bean attribute configuration, Spring MVC processing method input parameter binding and other occasions.

You can register a custom type converter through the converters property of the ConversionServiceFactoryBean.

2.2.2 converter types supported by spring

Spring has customized 3 types of converter interfaces. Any converter interface can be registered in the ConversionServiceFactoryBean as a custom converter

  • Converter < s, t >: converts an S-type object to a T-type object.
  • ConverterFactory: encapsulates multiple "homogeneous" converters in the same family. You can use this Converter factory class if you want to convert objects of one type to objects of another type and its subclasses (such as String to Number and Number subclass (Integer, Long, Double, etc.).
  • GenericConverter: type conversion will be performed according to the context information in the host class where the source class object and the target class object are located

2.2.3 steps for customizing the converter

Steps:

1) Implement Converter interface, write a Converter of custom type

2) configure ConversionService

3) let spring MVC use ConversionService

2.2.3 example of custom converter

Requirement: string to object

1. Definition page:

<form action="${ctp }/quickadd">

   <! -- write all employee information and automatically encapsulate the object -- >
   <input name="empInfo" value="empAdmin-admin@qq.com-1-101"/>
   < input type = "submit" value = "add quickly" / >
    
</form>

2. Controller method:

	
	/*
	 * Send data with request to work
	 * quick?empInfo=empAdmin-admin@qq.com-1-101
	 * You can write a custom type converter
	 */
	@RequestMapping("/quickadd")
	public String quickAdd(@RequestParam("empInfo")Employee employee){
		employeeDao.save(employee);
		return "redirect:/emps";
	}
	

3. Custom type Converter (implement Converter interface):

package com.test.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;

import com.test.bean.Employee;
import com.test.dao.DepartmentDao;




/*
 * Convert String to Employee
 */

public class MyStringToEmployeeConverter implements Converter<String, Employee>{

	@Autowired
	DepartmentDao dao;
	/*
	 * Custom conversion rules
	 */
	@Override
	public Employee convert(String source) {
	    System.out.println("String to be converted for page submission:"+source);
	    Employee employee=new Employee();
	    if(source.contains("-")){
	    	String[] split=source.split("-");
	    	employee.setLastName(split[0]);
	    	employee.setEmail(split[1]);
	    	employee.setGender(Integer.parseInt(split[2]));
	    	employee.setDepartment(dao.getDepartment(Integer.parseInt(split[3])));
	    }
		return employee;
	}

	

	

	

}

4. Configure ConversionService:

<! -- tell spring MVC not to use the defau lt ConversionService, but the custom ConversionService -- >
   <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <! -- converters add our custom type converter -- >
   <property name="converters">
    <set>
      <bean class="com.test.component.MyStringToEmployeeConverter"></bean>
    </set>
   </property>
   
   </bean>

5. Let spring MVC use ConversionService:

<! -- conversion service = "conversionservice" use your own configured type conversion component -- >
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

3, < MVC: annotation driven / >

3.1 timing of configuration

1. Directly configured page: it does not need to go through the controller to execute the results, but it will cause other request paths to fail, and the < MVC: annotation driven / > tag needs to be configured.

<mvc:view-controller path="/success" view-name="success"/>

2. During the restful crud operation, when deleting, when executing the delete request through JQuery, no static resources can be found, and the < MVC: annotation driven / > tag needs to be configured.

< MVC: default servlet handler / > will define a
 DefaultServletHttpRequestHandler, which will screen the requests entering the dispatcher Servlet. If it is found that the request is not mapped, it will be handed over to the default Servlet of the WEB application server for processing. If it is not a static resource request, the dispatcher Servlet will continue to process it.

3. When configuring the type converter service, you need to specify the conversion service reference.

< MVC: annotation driven conversion service = "conversionService" / > customized
 ConversionService is registered in the context of Spring MVC

3.2 powerful functions

Source code:

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {

	private static final boolean jsr303Present = ClassUtils.isPresent(
			"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

	private static final boolean jaxb2Present =
			ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

	private static final boolean jackson2Present =
			ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
					ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

	private static final boolean jacksonPresent =
			ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
					ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

	private static boolean romePresent =
			ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		Object source = parserContext.extractSource(element);

		CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
		parserContext.pushContainingComponent(compDefinition);

		RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);

		RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
		handlerMappingDef.setSource(source);
		handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		handlerMappingDef.getPropertyValues().add("order", 0);
		handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
		if (element.hasAttribute("enable-matrix-variables") || element.hasAttribute("enableMatrixVariables")) {
			Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute(
					element.hasAttribute("enable-matrix-variables") ? "enable-matrix-variables" : "enableMatrixVariables"));
			handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
		}

		RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
		RuntimeBeanReference validator = getValidator(element, source, parserContext);
		RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element, source, parserContext);

		RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
		bindingDef.setSource(source);
		bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		bindingDef.getPropertyValues().add("conversionService", conversionService);
		bindingDef.getPropertyValues().add("validator", validator);
		bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

		ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
		ManagedList<?> argumentResolvers = getArgumentResolvers(element, source, parserContext);
		ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, source, parserContext);
		String asyncTimeout = getAsyncTimeout(element, source, parserContext);
		RuntimeBeanReference asyncExecutor = getAsyncExecutor(element, source, parserContext);
		ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
		ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

		RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
		handlerAdapterDef.setSource(source);
		handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
		handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
		if (element.hasAttribute("ignore-default-model-on-redirect") || element.hasAttribute("ignoreDefaultModelOnRedirect")) {
			Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute(
					element.hasAttribute("ignore-default-model-on-redirect") ? "ignore-default-model-on-redirect" : "ignoreDefaultModelOnRedirect"));
			handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
		}
		if (argumentResolvers != null) {
			handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
		}
		if (returnValueHandlers != null) {
			handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
		}
		if (asyncTimeout != null) {
			handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
		}
		if (asyncExecutor != null) {
			handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
		}
		handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
		handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
		String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);

		String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
		RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
		uriCompContribDef.setSource(source);
		uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
		uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
		parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);

		RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
		csInterceptorDef.setSource(source);
		csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
		RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
		mappedCsInterceptorDef.setSource(source);
		mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
		mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
		String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);

		RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
		exceptionHandlerExceptionResolver.setSource(source);
		exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
		exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
		exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
		String methodExceptionResolverName =
				parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);

		RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
		responseStatusExceptionResolver.setSource(source);
		responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		responseStatusExceptionResolver.getPropertyValues().add("order", 1);
		String responseStatusExceptionResolverName =
				parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);

		RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
		defaultExceptionResolver.setSource(source);
		defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		defaultExceptionResolver.getPropertyValues().add("order", 2);
		String defaultExceptionResolverName =
				parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);

		parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
		parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
		parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
		parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
		parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
		parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
		parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));

		// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
		MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

		parserContext.popAndRegisterContainingComponent();

		return null;
	}
...
}

< MVC: annotation driven / > three bean s, RequestMappingHandlerMapping, RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver, will be registered automatically.

The following support is also provided:

  • Support the use of ConversionService instance for type conversion of form parameters
  • Support the use of @ NumberFormat, @ DateTimeFormat annotation to complete data type formatting
  • Support JSR 303 validation of JavaBean instances with @ Valid annotation
  • Support @ RequestBody and @ ResponseBody annotation

3.3 combined with source code analysis and configuration < MVC: default servlet handler / > < MVC: annotation driven / > why can static and dynamic resources be accessed?

1. Neither < MVC: default servlet handler / > nor < MVC: annotation driven / >

Dynamic resources (@ RequestMapping mapped resources) can be accessed, while static resources (. html,.js) cannot be accessed.

HandlerMapping:

Why dynamic resources can be accessed: the mapping information of each resource is saved in handlerMap in DefaultAnnotationHandlerMapping.

Static resource cannot be accessed because there is no request to save static resource mapping in handlerMap.

HandlerAdapter:

Without configuration, AnnotationMethodHandlerAdapter is the default factory setting, working (expired). In addition: conversionService is null (type converter does not work)

2. Configure < MVC: default servlet handler / > without < MVC: annotation driven / >

Static resources are accessible, but dynamic resources are not

Reason dynamic resources cannot be accessed: DefaultAnnotationHandlerMapping is not in HandlerMapping.

Why static resources can be accessed: simpleurhandlermapping maps all requests to Tomcat.

HandlerAdapter:

(3) Configure < MVC: default servlet handler / > and < MVC: annotation driven / > for static and dynamic access

Simpleurhandlermapping maps all requests to Tomcat, and static resources can be accessed.

RequestMappingHandlerMapping: dynamic resources can be accessed.

HandlerAdapter: AnnotationMethodHandlerAdapter is out of date. Spring 3.2 recommends RequestMappingHandlerAdapter to replace it. So, by default, when these two configurations are not configured, the HelloWorld program can run normally. However, when static resource searching is involved, the < MVC: annotation driven / > configuration must be configured

4, Format data

4.1 data format overview

In essence, formatting the input / output of attribute objects still belongs to the category of "type conversion". Spring defines a FormattingConversionService implementation class in the format module to implement the ConversionService interface. This implementation class extends GenericConversionService, so it has both type conversion and format functions.

FormattingConversionService has a FormattingConversionFactoryBean factory class, which is used to construct the former in the context of Spring. FormattingConversionFactoryBean has been registered internally:

  • NumberFormatAnnotationFormatterFactory: supports @ NumberFormat annotation for numeric type properties.
  • JodaDateTimeFormatAnnotationFormatterFactory: support @ DateTimeFormat annotation for date type properties

After assembling FormattingConversionFactoryBean, you can use annotation to drive (< MVC: annotation driven / > the ConversionService instance created by default is DefaultFormattingConversionService) when Spring MVC input parameter binding is model data output.

4.2 date format overview

@The DateTimeFormat annotation can annotate java.util.Date,java.util.Calender,java.long.Long time types:

  • pattern attribute: the type is string. Specify the model to parse / format field data, such as "yyyy MM DD".
  • ISO property: type is DateTimeFormat.ISO. Specifies the ISO model to parse / format field data, including four types: ISO.NONE (not used) - default, iso.date (yyyy MM DD), ISO.TIME(hh:mm:ss.SSSZ), iso.date ﹣ time (yyyy MM DD HH: mm: SS. Sssz).
  • Style property: string type. The format of date and time is specified by style, which consists of two characters. The first character represents the format of date, and the second character represents the format of time: S: short date / time format. M: Medium date / time format, L: long date / time format, F: full date / time format, -: ignore date or time format.

4.3 overview of value formatting

@NumberFormat can annotate attributes of similar number types. It has two mutually exclusive attributes:

  • Type: the type is NumberFormat.Style. Used to specify style types, including three types: Style.NUMBER (normal number type), Style.CURRENCY (currency type), Style.PERCENT (percentage type)
  • Pattern: the type is String, and the custom style, such as pattern = "ා, ා񖓿ාා"

4.4 experiment code (date format)

1. Page form

   birth:<form:input path="birth"/>       

2. Add date object attribute to employee class:

	private Date birth;

3. For format errors (the default supported format of the framework is slash method, 1992 / 09 / 09), set the format as 19992-09-09 on the page, and report errors:

4. To solve the 404 error, add the following to the date attribute of the Employee class:

	@DateTimeFormat(pattern="yyyy-MM-dd")

5. configuration

<! -- conversion service = "conversionservice" use your own configured type conversion component -- >
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

<! -- tell spring MVC not to use the defau lt ConversionService, but the custom ConversionService -- >
<! -- the ConversionService component created by ConversionService does not have a formatter -- >
<! -- use org.springframework.format.support.FormattingConversionServiceFactoryBean when writing custom type converters later
  It has both type conversion and format function -- >
   <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <! -- converters add our custom type converter -- >
   <property name="converters">
    <set>
      <bean class="com.test.component.MyStringToEmployeeConverter"></bean>
    </set>
   </property>
   
   </bean>

5, Data verification

We can know that it is not safe to only do front-end verification. On the one hand, you can bypass page submission requests, and on the other hand, you can disable JS verification. So we must add back-end verification to important data.

1) You can write a program to take out every data for verification. If it fails, you can directly go to the add page and prompt to fill in again. (no)

2) Spring MVC can use JSR303 for data verification.

JSP303:: specification -- hibernate validator (third party validation framework)

5.1 JSR 303 

JSR 303 is Java's standard framework for bean data validity, which has been included in Java EE6.0. JSR 303 (Java Specification Request means Java specification proposal) specifies the verification rules by annotating the bean attributes with standard annotations such as @ NotNull, and verifies the bean through the standard verification interface.

5.2 Hibernate Validator extension annotation

Hibernate Validator is a reference implementation of JSR 303. In addition to supporting all standard validation annotations, it also supports the following extension annotations.

5.3 Spring MVC data verification

Spring 4.0 has its own independent data verification framework and supports JSR 303 standard verification framework. When spring performs data binding, it can call the verification framework to complete data verification at the same time. In Spring MVC, you can directly verify data through annotation driven method.

Spring's LocalValidatorFactoryBean implements both spring's Validator interface and JSR 303's Validator interface. As long as you define a LocalValidatorFactoryBean in the spring container, you can inject it into the Bean that needs data validation.

Spring itself does not provide the implementation of JSR 303, so you must put the jar package of JSR 303 implementer into the classpath. < MVC: annotation driven / > a localvalidatorfactorbean will be assembled by default. By labeling @ Valid annotation on the input parameter of the processing method, Spring MVC can perform data validation after data binding. In the form / command object annotated with JSR 303, mark @ Valid. After the Spring MVC framework requests parameters to bind the input parameter object, it will call the validation framework to implement validation according to the validation rules declared by the annotation.

5.4 experiment code

1) Import the jar package of the validation framework.

hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar

2) Add validation notes to validation properties

	private Integer id;
	
	@NotEmpty
	@Length(min=6,max=18)
	private String lastName;

	@Email
	private String email;
	//1 male, 0 female
	private Integer gender;
	
	private Department department;
	
	//It must be a time in the past
	@DateTimeFormat(pattern="yyyy-MM-dd")
	@Past
	private Date birth;
	
	

3) When spring MVC encapsulates objects, tell spring MVC that this JavaBean needs to be verified

@RequestMapping(value="/emp",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee){

4) How to know the verification result: the JavaBean to be verified is followed by a BindingResult, which encapsulates the verification result of the previous bean.

@RequestMapping(value="/emp",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee,BindingResult result){

5) What to do according to different verification results? If validation fails, go back to the add page

	@RequestMapping(value="/emp",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee,BindingResult result){
		System.out.println("Employees to add"+employee);
	boolean hasErrors=result.hasErrors();
	if(hasErrors)
	{
		System.out.println("Check error");
		return "add1";
	}else{
		employeeDao.save(employee);
		//Redirect query all employees
		return "redirect:/emps";
	}
	}

6) After the verification fails, use < form: Errors / > to prompt. Spring MVC not only saves the validation results of the form / command object to the corresponding BindingResult or Errors object, but also saves all the validation results to the "hidden model". Even if there is no result input parameter corresponding to the form / command object in the signature of the processing method, the verification result will be saved in the implicit model. All the data in the implicit model will eventually be exposed to the JSP view object through the attribute list of HttpServletRequest, so the error information can be obtained in the JSP. In JSP page, error message can be displayed through < form: Errors path = "" / >.

<form:form action="${ctp }/emp"  modelAttribute="employee"  method="POST">
     <! -- Path: the name of HTML input 
                                     (1) As a native name item
                                       (2) Automatically echo the attribute value of an object in the implicit model
          -->
    lastName:<form:input path="lastName"/><form:errors path="lastName"/><br/>
    email:<form:input path="email"/><form:errors path="email"/><br/>
    gender:<br/>
                            Male: < form: RadioButton path = "gender" value = "1" / > < br / >
                            Female: < form: RadioButton path = "gender" value = "0" / > < br / >
   <! -- items: specify the collection to traverse, traverse automatically, and each element traversed is a department object
        Itemlabel = "property name": specifies which property of the object to traverse as the value of the option label body
        itemValue = "property name": specifies which property of the object to traverse as the submitted value
    
    -->
     birth:<form:input path="birth"/><form:errors path="birth"/><br/>              
   dept:<form:select path="department.id" items="${depts }" itemLabel="departmentName" itemValue="id">
   <br/>
   </form:select><br/>
   < input type = "submit" value = "save" / > 
</form:form>

After the verification fails, the page displays:

5.5 BindingResult

public interface BindingResult extends Errors

Spring MVC saves the verification result by signing the processing method specification: the verification result of the previous form / command object is saved in the subsequent input parameter, which must be of BindingResult or Error type, both of which are located in the org.springframework.validation package.

The Bean object to be verified and its binding result object appear in pairs, and other input parameters are not allowed to be declared between them.

The Errors interface provides methods to get error information, such as getErrorCount(). BindingResult extends the Errors interface.

How can native forms extract error information? Put the error in the request domain, use BindingResult to get the error message, and put the error message in the Model.

@RequestMapping(value="/emp",method=RequestMethod.POST)
	public String addEmp(@Valid Employee employee,BindingResult result,Model model){
		System.out.println("Employees to add"+employee);
	boolean hasErrors=result.hasErrors();
	Map<String,Object> errorMap=new HashMap<String,Object>();
	if(hasErrors)
	{
		
		List<FieldError> errors=result.getFieldErrors();
		for(FieldError fieldError:errors)
		{
			System.out.println("Error message prompt"+fieldError.getDefaultMessage());
			System.out.println("The error field is"+fieldError.getField());
			System.out.println(fieldError);
		     errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
			
		}
		model.addAttribute("errorInfo", errorMap);
		System.out.println("Check error");
		return "add1";
	}else{
		employeeDao.save(employee);
		//Redirect query all employees
		return "redirect:/emps";
	}
	}

Form display:

<form:form action="${ctp }/emp"  modelAttribute="employee"  method="POST">
     <! -- Path: the name of HTML input 
                                     (1) As a native name item
                                       (2) Automatically echo the attribute value of an object in the implicit model
          -->
    lastName:<form:input path="lastName"/><form:errors path="lastName"/>-->${errorInfo.lastName }<br/>
    email:<form:input path="email"/><form:errors path="email"/>-->${errorInfo.email }<br/>
    gender:<br/>
                            Male: < form: RadioButton path = "gender" value = "1" / > < br / >
                            Female: < form: RadioButton path = "gender" value = "0" / > < br / >
   <! -- items: specify the collection to traverse, traverse automatically, and each element traversed is a department object
        Itemlabel = "property name": specifies which property of the object to traverse as the value of the option label body
        itemValue = "property name": specifies which property of the object to traverse as the submitted value
    
    -->
     birth:<form:input path="birth"/><form:errors path="birth"/>-->${errorInfo.birth }<br/>              
   dept:<form:select path="department.id" items="${depts }" itemLabel="departmentName" itemValue="id">
   <br/>
   </form:select><br/>
   < input type = "submit" value = "save" / > 
</form:form>

After the validation fails, the following is displayed:

5.6 display of custom internationalization error messages

Each property will form a corresponding FieldError object when there are errors in data binding and data validation.

When an attribute fails to be verified, the verification framework will generate 4 message codes for the attribute. These codes are prefixed with the annotation class name and combined with the modleAttribute, attribute name and attribute type name to generate multiple corresponding message codes: for example, the password attribute in the User class is marked with an @ Pattern annotation. When the attribute value does not meet the rules defined by @ Pattern, the following will be generated: 4 error codes:

  • Pattern.user.password
  • Pattern.password
  • Pattern.java.lang.String
  • Pattern

When using the Spring MVC tag to display errors, Spring MVC will check whether the WEB context is equipped with the corresponding internationalization message. If not, the default error message will be displayed. Otherwise, the nationalization message will be used.

If there is an error in data type conversion or data format conversion, or some parameters do not exist, or an error occurs when calling the processing method, an error message will be created in the implicit model. The prefix of the error code is as follows:

  • Required: the required parameter does not exist.
  • typeMisMatch: data type mismatch occurs during data binding
  • methodInvocation: Spring MVC encountered an error while calling a handler

5.6.1 experiment code

1. Prepare international documents:

errors_en_US.properties:

Email.email=email incorrect~~
NotEmpty=must not empty~~
Length.java.lang.String=length incorrect~~
Path=must a past time
typeMismatch.employee.birth=birth geshi buzhengque

errors_zh_CN.properties:

Email.email=\u90AE\u7BB1\u9519\u4E86\uFF01
NotEmpty=\u4E0D\u80FD\u4E3A\u7A7A
Length.java.lang.String=\u957F\u5EA6\u4E0D\u5BF9
Path=\u65F6\u95F4\u5FC5\u987B\u662F\u8FC7\u53BB\u7684
typeMismatch.employee.birth=\u751F\u65E5\u683C\u5F0F\u4E0D\u6B63\u786E

2. Let Spring MVC manage international resource files.

 <!-- Manage national resource documents -->
   <bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messgaeSource">
      <property name="basename" value="errors"></property>
   
   </bean>

3. Access to the page

4. Advanced internationalization, dynamic input parameters

Length.java.lang.String=length incorrect,must between {2} and {1}~~
//Element 0 is the current attribute name
//Element 12 is sorted alphabetically

 

Published 90 original articles, won praise 11, visited 20000+
Private letter follow

Posted by saeed42 on Wed, 05 Feb 2020 22:31:22 -0800