如何使用 BindingResult 检查特定行是否存在 Spring 中的多行验证的验证错误

How to check if a particular row has validation error for a multi row validation in Spring using BindingResult

当前逻辑会检查BindingResult是否有错误,并在jsp中显示数据和错误。 所需的逻辑 是检查每一行的错误并仅显示包含验证错误的行并更新没有验证错误的行。 @Autowired private IncidentExtractStgService incidentExtractStgService;

@RequestMapping(value = "/validatingIncidentList", method = RequestMethod.POST)
public String ValidateIncidentList( @Valid @ModelAttribute("incidentsForm") IncidentsForm incidentsForm,
        BindingResult bindingResult,RedirectAttributes redirectAttributes) {
    if (bindingResult.hasErrors()) {


        for(ObjectError error: bindingResult.getAllErrors()){

            System.out.println(error);
        }

        redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", bindingResult);
        redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);

        return "redirect:/validateIncidentList";
    }
    else
    {
        for(IncidentExtractStg ie : incidentsForm.getIncidents()) {

            ie.setValidated(1);
            incidentExtractStgService.update(ie);

            System.out.println(ie.getNumber()+"     "+ie.getWaitTime());
        }


    return  "redirect:/validateIncidentList";

    }

下面的代码片段将检查模型是否包含属性 "incidetsForm",如果是,则将其发送到 example.jsp,后者将显示数据和验证错误。

@RequestMapping(value = "/validateIncidentList", method = RequestMethod.GET)
 public String incidentList(Model model) {
    if (!model.containsAttribute("incidentsForm")) {
            List<IncidentExtractStg> incidents = incidentExtractStgDao.validateList();
            incidentsForm.setIncidents(incidents);
            model.addAttribute("incidentsForm", incidentsForm);
            return "example";
    }

     model.addAttribute("errormessage","Please Check the Validation Errors column for Errors");
     return "example";
}

Example.jsp 代码片段

<c:forEach var="ie" items="${incidentsForm.incidents}" varStatus="status">
             <tr>
                  <td><form:input path="incidents[${status.index}].id" value="${ie.id}" readonly ="true"/></td>
                 <td><form:errors path="incidents[${status.index}].id" cssClass="error" /></td> 

                <td><form:input path="incidents[${status.index}].number" value="${ie.number}"/></td>
                <td><form:errors path="incidents[${status.index}].number" cssClass="error" /></td> 
            </tr>

IncidentsForm.java:

import java.util.List;
import javax.validation.Valid;

import com.infosys.sla.model.IncidentExtractStg;

public class IncidentsForm {

@Valid
private List<IncidentExtractStg> incidents;



public List<IncidentExtractStg> getIncidents() {
    return incidents;
}


public void setIncidents(List<IncidentExtractStg> incidents) {

    this.incidents = incidents;
}
}

IncidentExtractStg.java 片段

@Entity
@Table(name="incident_extract_stg")
public class IncidentExtractStg {

@Id
@Column(name="ies_id")
private int id;

@NotBlank(message="number cannot be empty")
@Pattern(regexp="[A-Za-z0-9]*",message="number can contain only alphabets and numbers")
@Column(name="ies_number")
private String number;

首先,如果我是你,我将提取服务层内的所有逻辑。要继续,您可以创建一个接口 IncidentService 及其自己的具体实现 IncidentServiceImpl,您可以在其中安全地处理您的需求。控制器 绝对 没有完成所有事情。

那么,你的需求是什么? "check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors"

服务层中的方法可能是这样的:

public void handleErrors(IncidentsForm incidentsForm, BindingResult bindingResult){ 

    List<String> fieldsInErrorState = new ArrayList<String>(10);

    if (bindingResult.hasErrors()) { //
        Map<String, Object> bindingModel = bindingResult.getModel();

        for (Map.Entry<String, Object> entry : bindingModel.entrySet()) {
            String key = entry.getKey();
            //Object value = entry.getValue(); you don't need to parse that unless you want specific domain model handlers to run

            //you need to store the key as a form field which is in error state
            fieldsInErrorState.add(key);

            //you already have all the stuff to parse and display errors in your JSP
            //thanksfully to bindingResult and JSTL tags.
        }

        ContactMessageForm cmForm2 = new ContactMessageForm();
        // get the list of the fields inside your form
        Field[] declaredFields = ContactMessageForm.class.getDeclaredFields();
        for (Field field : declaredFields) {
            if (!fieldsInErrorState.contains(field.getName())) {
                if (field.getName().equalsIgnoreCase("firstname")) {
                    cmForm2.setFirstname(contactMessageForm.getFirstname());
                }
                if (field.getName().equalsIgnoreCase("lastname")) {
                    cmForm2.setLastname(contactMessageForm.getLastname());
                }

                //etc for each properties of your form object.
            }

            // then store your dbmodel object
            // BUT i think you must be carefull to your data integrity... It is maybe not safe to save an object like that with bypassing some stuff... 
            // Your form was built like that maybe for a good reason looking at your objects graph.
            // If your form is too big, then split it in small parts, it will be much easy to handle, to update, and to work with daily.
        }

    }


}

当然,您需要自定义该代码,不要忘记将 throws IntrospectionException 添加到您的服务方法中,这样就很好了。

干杯!

由于逻辑是只显示那些包含验证错误的行,因此创建了一个新列表来存储至少有一个验证错误的行。

创建一个新的 BindingResult 以根据新列表的索引存储错误。(如果不这样做,则错误消息将不会显示在显示的行上)。

下面的逻辑是针对行的每个字段检查字段错误,从 jsp 您可以看到字段名称有 "incidents[${status.index}].id"。 - 计数器 i 获取行数 - 计数器j是为BindingResult设置索引。

BeanPropertyBindingResult result2 = new BeanPropertyBindingResult(incidentsForm, bindingResult.getObjectName();

    List<IncidentExtractStg> incidents= new ArrayList<IncidentExtractStg>();
    int i=0;// to get the row count
    int j=0;// to set the index 
    for(IncidentExtractStg ies : incidentsForm.getIncidents()) 
    {    
            int count=0;
            Field[] declaredFields = IncidentExtractStg.class.getDeclaredFields();
            for (Field field : declaredFields) 
            {
                if (bindingResult.hasFieldErrors("incidents["+i+"]."+field.getName()))
                {
                  for (FieldError error: bindingResult.getFieldErrors("incidents["+i+"]."+field.getName()))
                    {
                     result2.addError(new FieldError(error.getObjectName(), "incidents["+j+"]."+field.getName(), error.getRejectedValue(), error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage()));   
                    }
                 count++;
                }
            }

            if(count>0)
            {
                 j++;
                 incidents.add(ies);    
            }
            else 
            {
                ies.setValidated(1);
                incidentExtractStgService.update(ies);  
            }

        i++;
    }

    i=0;
    j=0;

    if (bindingResult.hasErrors()) {

        incidentsForm.setIncidents(incidents);
        System.out.println("error block");

        for (FieldError error: result2.getFieldErrors()) {
           System.out.println("field errors are  "+error.getField());
           System.out.println("field errors are  "+error);
        }

        redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", result2);
        redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
        return "redirect:/validateIncidentList";
    }       

例如:如果在第 30 行,incidents[30].number 字段有验证错误。然后 i=30 且 count>0 且 j=0。

因此,整行事件[30] 将保存在新初始化列表中的索引 0 处,绑定结果也将添加到索引 0 处。如果 J 未设置为 result2 并且使用绑定结果,则它仍将指向索引 30,并且不会针对现在存储在索引 0 的字段显示错误消息。

现在,更新后的 IncidentsForm 和 result2 将被发送到 jsp 页面,该页面将仅显示那些有验证错误的行以及相应的错误消息。