开发者

JSR 303 Bean Validation + Javascript Client-Side Validation

开发者 https://www.devze.com 2022-12-24 06:39 出处:网络
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 u开发者_JS百科sing Sp

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 u开发者_JS百科sing 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

0

精彩评论

暂无评论...
验证码 换一张
取 消