English 中文(简体)
JSR 303 Bean Validation + Javascript Client-Side Validation
原标题:

What is the best way to perform client-side form validation using Javascript (with minimal code duplication) when using JSR 303 bean validation on the server side? I m currently using Spring 3 and the Hibernate Validator.

问题回答

I would suggest that you look at Spring JS, which relies heavily on Dojo. A tutorial can be found here.

Easiest way for yourself to start playing with it is to download Spring Roo, create the petclinic sample application with one of the example-scripts (this takes you 5 minutes) and then play around with how the javascript is integrated. Spring Roo creates an app with the same technology stack that you use (Spring+hibernate+implementation of jsr 303)

I found this open source project but it looks dead, maybe it is worth resurrecting.

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

This library provides client side validation of an HTML form based on JSR-303 and Hibernate Validator annotations, integrated with Spring MVC. The library provides a JavaScript validation code base that handles basic interaction with HTML forms, as well as JavaScript functions implementing the validation annotations supported by Hibernate Validator (including those not from the JSR-303 spec). This JavaScript code base can be included in a page by using a provided taglib or by extracting the JavaScript file from the jar and including it using a tag. Once this code base has been included in a page, a second taglib is used to generate the JavaScript code for validating an HTML form. You can also provide a JSON object in the body of the tag to specify additional configuration information.

Here s how I m doing it using Spring MVC + JQuery + Bootstrap, partially based on 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
}

address.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>

It could use some refactoring, but this will do an ajax GET with the form data and update the page based on the result.

Richfaces supports this. They have a small demo on their site.

PrimeFaces Client Side Validation Framework Supports Bean Validation.

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

Here is an open source alternative to JSR-303.

This solution can perform all validation of the request message, but no hassle coding is required.

https://github.com/ckpoint/CheckPoint

With Check-Point, all validation is possible without further code, just by changing the annotation of the Controller method.

After that, all validation settings can be easily made in Admin Page.

I think this video can help your understand. https://youtu.be/I1aEIztXlhE

Check-Point Admin-Page Setting Screen

Edit:

Indeed JSR 303 is the best way (IMO) to handle client side validation. The best thing is that if you have proper js libraries on the fronted you can use the same validation (the same code) on the server (if you would use node.js). I ve created library @stopsopa/validation for this purposes I m validating forms like this (In 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"));

live example is available here: https://codesandbox.io/s/ymwky9603j





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签