JSR 303 Bean 验证 + Javascript 客户端验证

Posted

技术标签:

【中文标题】JSR 303 Bean 验证 + Javascript 客户端验证【英文标题】:JSR 303 Bean Validation + Javascript Client-Side Validation 【发布时间】:2011-01-31 14:45:09 【问题描述】:

在服务器端使用 JSR 303 bean 验证时,使用 javascript 执行客户端表单验证(代码重复最少)的最佳方式是什么?我目前正在使用Spring 3 和Hibernate Validator。

【问题讨论】:

【参考方案1】:

我建议您查看严重依赖 Dojo 的 Spring JS。教程可以在here找到。

开始使用它的最简单方法是下载Spring Roo,使用示例脚本之一创建宠物诊所示例应用程序(这需要您 5 分钟),然后尝试如何集成 javascript。 Spring Roo 使用您使用的相同技术堆栈创建应用程序(Spring+hibernate+jsr 303 的实现)

【讨论】:

【参考方案2】:

我找到了这个开源项目,但它看起来已经死了,也许值得复活。

http://kenai.com/projects/jsr303js/pages/Home

该库提供基于 JSR-303 和 Hibernate Validator 注释的 html 表单的客户端验证,并与 Spring MVC 集成。该库提供了一个 JavaScript 验证代码库,用于处理与 HTML 表单的基本交互,以及实现 Hibernate Validator 支持的验证注释的 JavaScript 函数(包括那些不来自 JSR-303 规范的那些)。该 JavaScript 代码库可以通过使用提供的 taglib 或通过从 jar 中提取 JavaScript 文件并使用标记包含它来包含在页面中。一旦这个代码库被包含在一个页面中,第二个 taglib 被用来生成 JavaScript 代码来验证一个 HTML 表单。您还可以在标签正文中提供 JSON 对象以指定其他配置信息。

【讨论】:

肯定会测试这个。在 bean 上设置验证并将其自动复制到客户端是可行的方法【参考方案3】:

这是我使用Spring MVC + JQuery + Bootstrap 的方法,部分基于a recent blog post at SpringSource:

AddressController.java

@RequestMapping(value="/validate")
public @ResponseBody ValidationResponse processForm(Model model, @Valid AddressForm addressForm, BindingResult result) 
    ValidationResponse res = new ValidationResponse();
    if (result.hasErrors()) 
        res.setStatus("FAIL");
        for (ObjectError error : result.getAllErrors()) 
            if (error instanceof FieldError) 
                FieldError fieldError = (FieldError) error;
                res.addError(fieldError.getField(), fieldError.getDefaultMessage());
                
        
    
    else 
        res.setStatus("SUCCESS");
    
    return res;

AddressForm.java

public class AddressForm 
    @NotNull
    @Size(min=1, max=50, message="Address 1 is required and cannot be longer than max characters")
    private String address1;

    @Size(max=50, message="Address 2 cannot be longer than max characters")
    private String address2;

    // etc

ValidationResponse.java:

public class ValidationResponse 
    private String status;
    private Map<String,String> errors;
    // getters, setters

地址.jsp:

<f:form commandName="addressForm">
    <div class="control-group">
        <label for="address1">Address 1</label>
        <div class="controls">
            <f:input path="address1" type="text" placeholder="Placeholder Address 1" class="wpa-valid" />
            <span class="help-inline"></span>
        </div>
    </div>
    <!-- etc -->
    <div class="form-actions">
        <button type="submit" class="btn btn-primary">Save</button>
        <button type="button" class="btn">Cancel</button>
    </div>
</f:form>

<script type="text/javascript">
function collectFormData($fields) 
    var data = ;
    for (var i = 0; i < $fields.length; i++) 
        var item = $($fields[i]);
        data[item.attr("id")] = item.val();
    

    return data;


function clearErrors($fields) 
    for (var i = 0; i < $fields.length; i++) 
        var item = $($fields[i]);
        $("#"+item.attr("id")).parents(".control-group").removeClass("error");
        $("#"+item.attr("id")).siblings(".help-inline").html("");
    


function markErrors(errors) 
    $.each(errors, function(key, val) 
        $("#"+key).parents(".control-group").addClass("error");
        $("#"+key).siblings(".help-inline").html(val);
    );


$(document).ready(function() 
    var $form = $("form.validate");
    $form.bind("submit", function(e) 
        var $fields = $form.find(".validate");

        clearErrors($fields);
        var data = collectFormData($fields);

        var validationUrl = "validate";
        $.get(validationUrl, data, function(response) 
            $("#alert").removeClass();

            if (response.status == "FAIL") 
                markErrors(response.errors);

                $("#alert").addClass("alert alert-error");
                $("#alert").html("Correct the errors below and resubmit.");
             else 
                $("#alert").addClass("alert alert-success");
                $("#alert").html("Success!");

                $form.unbind("submit");
                $form.submit();
            
        , "json");

        e.preventDefault();
        return false;
    );
);
</script>

它可以使用一些重构,但这将使用表单数据执行 ajax GET 并根据结果更新页面。

【讨论】:

【参考方案4】:

Richfaces 支持这一点。他们有一个small demo on their site。

【讨论】:

【参考方案5】:

PrimeFaces 客户端验证框架支持 Bean 验证。

http://blog.primefaces.org/?p=2874

【讨论】:

【参考方案6】:

这是 JSR-303 的开源替代方案。

此解决方案可以执行请求消息的所有验证,但无需繁琐的编码。

https://github.com/ckpoint/CheckPoint

使用 Check-Point,只需更改 Controller 方法的注解,即可进行所有验证,无需进一步的代码。

之后,所有验证设置都可以在管理页面中轻松进行。

我认为这个视频可以帮助您理解。 https://youtu.be/I1aEIztXlhE

Check-Point Admin-Page Setting Screen

【讨论】:

【参考方案7】:

编辑:

确实,JSR 303 是处理客户端验证的最佳方式 (IMO)。最好的事情是,如果你在前端有适当的 js 库,你可以在服务器上使用相同的验证(相同的代码)(如果你使用 node.js)。 为此,我创建了库 @stopsopa/validation 我正在验证这样的表单(在 React.js 中):

import React,  Component  from "react";
import ReactDOM from "react-dom";

import validator, 
  Collection,
  Required,
  Optional,
  NotBlank,
  Length,
  Email,
 from "@stopsopa/validator";

class App extends Component 
  constructor(...args) 
    super(...args);
    this.state = 
      data: 
        name: "",
        email: ""
      ,
      errors: ,
      validate: false
    ;
  
  onSubmit = async e => 
    e.preventDefault();

    const errors = await validator(this.state.data, new Collection(
      name: new Required([
        new NotBlank(),
        new Length(min: 3),
      ]),
      email: new Required([
        new NotBlank(),
        new Email(),
      ])
    ));
    this.setState(
      errors: errors.getFlat(),
      validate: true,
    );

    if ( ! errors.count()) 

      console.log('send data to server', this.state.data);
    
  ;
  onChange = (name, value) => 
    this.setState(state => (
      ...state,
      data:  ...state.data, ... [name]: value  
    ));
  ;
  render() 
    const s = this.state;
    return (
      <form onSubmit=this.onSubmit>
        <label>
          name:
          <input
            value=s.data.name
            onChange=e => this.onChange("name", e.target.value)
          />
        </label>
        s.validate && s.errors.name && (
          <div className="error">s.errors.name</div>
        )
        <br />
        <label>
          email:
          <input
            value=s.data.email
            onChange=e => this.onChange("email", e.target.value)
          />
        </label>
        s.validate && s.errors.email && (
          <div className="error">s.errors.email</div>
        )
        <br />
        <input type="submit" value="submit" />
      </form>
    );
  


ReactDOM.render(<App />, document.getElementById("root"));

现场示例可在此处获得:https://codesandbox.io/s/ymwky9603j

【讨论】:

以上是关于JSR 303 Bean 验证 + Javascript 客户端验证的主要内容,如果未能解决你的问题,请参考以下文章

JSR 303 Bean 验证 - 为啥使用 getter 而不是 setter?

Wildfly:ExceptionMapper 未通过 RestEasy JSR-303 Bean 验证触发

带有 i18n 消息的 Spring-MVC + RESTeasy 表单 bean 验证 (JSR 303)

SpringMVC中的 JSR 303 数据校验框架说明

JSR 303标准

JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践