【问题标题】:How to use javax.validation and JSON request in Spring 4 MVC?如何在 Spring 4 MVC 中使用 javax.validation 和 JSON 请求?
【发布时间】:2015-02-04 02:32:50
【问题描述】:

我正在使用 Spring 4 MVC 开发一个 Web 应用程序。我想知道我是否可以使用 javax.validation API 验证 JSON 请求对象。例如,我有这段 entity 代码:

    ...       
    @JsonProperty("cheFecha")
    @NotNull
    @Column(name = "che_fecha")
    @Temporal(TemporalType.DATE)
    @DateTimeFormat(style = "M-")
    private Date SsiCheque.cheFecha;

    @JsonProperty("cheMonto")
    @NotNull
    @JsonSerialize(using = CurrencySerializer.class)
    @Column(name = "che_monto", precision = 10, scale = 2)
    private BigDecimal SsiCheque.cheMonto;
    ...

我有控制器代码:

@RequestMapping(value = "/addCheck", method = RequestMethod.POST)
public @ResponseBody SsiCheque addChecks(@Valid SsiCheque ssiCheque, BindingResult result) {

    //ssiCheque.persist();
    System.out.println("add" + result.getErrorCount());// Zero when there are errors
    return ssiCheque;
}

最后我得到了 jQuery 代码:

    var formData = $("#formAddChecks :input").serializeArray();
    $.ajax({
        type: "POST",
        url: "addCheck",
        data: formData,
        beforeSend: function ( xhr ) {
            console.log("before Send");
        },
        error: function (request, status, error) {            
            console.log('Error ' + "\n" + status + "\n" + error);
        },
        success: function(data) {
            console.log(data);
        }
    });

JSON 对象正确到达控制器,但我想使用实体 javax.annotations API 验证 JSON。我所看到的只是使用 自定义验证器 并“重写”验证代码。

这是验证 JSON 的唯一方法吗?

提前致谢!

更新 1

我遵循了@James Massey 的建议,我的代码现在看起来像这样:

控制器

@RequestMapping(value = "/addCheck", method = RequestMethod.POST)
@ResponseBody
public SsiCheque addChecks(@Valid @RequestBody SsiCheque ssiCheque, BindingResult result) {

    //ssiCheque.persist();
    System.out.println("agregar " + result.getErrorCount());
    return ssiCheque;
}

Javascript 文件

    var ssiCheque = {
            cheNumero : $("#formAddChecks cheNumero").val(),
            cheRecepto : $("#formAddChecks cheReceptor").val(),
            cheMonto : $("#formAddChecks cheMonto").val(),
            cheFecha : $("#formAddChecks cheFecha").val(),
            cheConcepto : $("#formAddChecks cheConcepto").val()
    };


    $.ajax({
        type: "POST",
        contentType: "application/json",
        url: "addCheck",
        data: ssiCheque,
        dataType: "json",
        beforeSend: function ( xhr ) {
            console.log("before Send");
        },
        error: function (request, status, error) {            
            console.log('Error ' /*+ request.responseText*/ + "\n" + status + "\n" + error);
        },
        success: function(data) {
            console.log(data);
        }
    });

但是当我提交表单并执行 Ajax 功能 时,我收到了 400 错误(请求不正确)。我之前在json对象格式和控制器规格不兼容时遇到过这个错误,但这次我不知道为什么会出现这个错误。

再次感谢!

【问题讨论】:

  • 使用 Jackson(类似)将 JSON 映射到对象,然后正常调用验证有什么问题?问题是你的绑定结果在传递无效数据时没有任何错误吗?
  • 我在 cheFecha 字段中放了一个 @NotNull 注释,当我测试时,我发送 cheFecha null 并且它似乎正在跳过验证部分,因为我在 getErrorCount() 方法中为零
  • 我认为它会被你奇怪的对象组合弄糊涂。从日期字段中删除 SsiCheque
  • 基本上是因为你的SsiCheque 对象不为空,满足约束,它不会更深入地检查对象。
  • 我忘了说我的实体是由 Spring Roo 生成的,所以代码 SsiCheque.cheFecha 是生成代码的一部分。我不太了解,但我读过那是面向方面的编程(AOP)

标签: java jquery json spring validation


【解决方案1】:

我以另一种方式解决了我的验证问题。假设我有和代理对象:

public class Agent {
    public int userID;
    public String name;
    public boolean isVoiceRecorded;
    public boolean isScreenRecorded;
    public boolean isOnCall;
}

我想验证: (1) 用户ID>0 (2) 姓名为必填项 (3) isVoiceRecorded 和 isScreenRecorded 只有在 isOnCall 为真时才为真。

为此,我需要添加依赖项:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>

现在看看 Agents 类的样子:

@NoArgsConstructor
@ToString
@EqualsAndHashCode(of = "userID")
@CheckBools
public class Agent {
    @Min(0)
    public int userID;
    @NotNull(message = "Name cannot be null")
    public String name;
    public boolean isVoiceRecorded;
    public boolean isScreenRecorded;
    public boolean isOnCall;
    public LocalDateTime startEventDateTime;
}

(1) @Min(0) - 解决 userID>0 (2) @NotNull(message = "Name cannot be null") - 解决名称是强制性的,并且您有示例如何指定错误消息 (3) 我定义的@CheckBools注解,在类级别检查isVoiceRecorded和isScreenRecorded只有在isOnCall为真时才能为真。

@Documented
@Constraint(validatedBy = MyConstraintValidator.class)
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface CheckBools {
    String message() default "'isVoiceRecorded' or 'isScreenRecorded' can be true only if you are on call";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

在下面的类中定义规则

public class MyConstraintValidator implements ConstraintValidator<CheckBools, Agent> {
     @Override
    public void initialize(CheckBools constraintAnnotation) {

    }

    @Override
    public boolean isValid(Agent value, ConstraintValidatorContext context) {
        if (!value.isOnCall && (value.isVoiceRecorded || value.isScreenRecorded))
            return false;
        else return true;
    }
}

在控制器级别:

@RestController
@RequestMapping("Myteamview")
public class MyteamviewController {
    @Autowired
    AgentInfo agentInfo;

    @RequestMapping(path = "agents", method = RequestMethod.POST)
    public ResponseEntity<Boolean> addOrUpdateAgent(@Valid @RequestBody Agent agent) {
        ResponseEntity<Boolean> responseEntity = new ResponseEntity<>(agentInfo.addAgent(agent),HttpStatus.OK);
        return responseEntity;
    }
}

注意:重要的是在@RequestBody Agent 之前指定@Valid

【讨论】:

    【解决方案2】:

    这里似乎有一些问题:

    1. 您的对象结构看起来很奇怪。为什么您的字段引用对象类型? private Date SsiCheque.cheFecha 似乎是一个完全没有意义的领域。

    2. 您通常将 UI 设计为通过可直接映射到 Java 对象的 JSON 对象进行发送。因此,如果您的对象看起来像这样:

      public class Example { 
          @NotNull
          @Digits(fraction = 2, integer = 10)
          private Integer foo;
          @NotEmpty
          private String bar;
          @NotEmpty
          private String[] baz;
      }
      

      那么你的 JSON 结构会是这样的:

      {
          "example": {
              "foo": 1,
              "bar": "Pineapple",
              "baz": [
                  "This is a string",
                  "So is this"
              ]
          }
      }
      

    Jackson 可以使用它直接映射到您的对象中。

    然后,假设您的项目类路径中包含 Jackson JAR,您将像这样编写控制器方法:

    @RequestMapping(value = "/example", method = RequestMethod.POST)
    @ResponseBody
    public Example(@Valid @RequestBody Example example, BindingResult result) {
         if(result.hasErrors()){
             //A validation has failed, return an error response to the UI
         } else {
             exampleService.createOrUpdate(example);
             return example;
         }
    }
    

    重要的部分是您的对象是请求正文,并且您使用 @RequestBody 注释,因为 Jackson 使用此作为信号来使用 HTTP 请求正文中存在的 JSON 构造您的对象。此方法的唯一缺点是您可能必须以编程方式构造请求 JSON。然而,这对 JavaScript 来说是微不足道的。 (我将在这里假设一些合理的输入 id 默认值,并且您熟悉 jQuery DOM 操作/选择语法)

    var bazArray = [];
    $.forEach($("#bazContainer"), function (baz, i){
        bazArray.push(baz);
    });
    var example = {
        foo: $("#fooInput").val(),
        bar: $("#barInput").val(),
        baz: bazArray
    };
    

    您在数据字段中将您的示例对象传递给您的请求,如果您指定它的类型为application/json,那么jQuery 将自动在您的示例对象上调用JSON.stringify。 希望这一切都有意义。

    【讨论】:

      【解决方案3】:

      解决方案(由提问者更新:Jessai)

      我检查了这个问题:Spring MVC 400 Bad Request Ajax

      总结一下我做了什么:

      1. 创建要使用 JSON.stringify 解析的对象并将其发送到控制器。

      2. 在控制器中,我按照@James Massey 所说,使用@ResponseBody 和@RequestBody 设置方法。

      3. 在实体中,我向字段添加了@JSONProperty(我已经拥有这些)和@JSONIgnore(我添加到 cheId 字段)注释。

      Javascript:

          var ssiCheque = {
                  cheNumero : $("#formAddChecks #cheNumero").val(),
                  cheRecepto : $("#formAddChecks #cheReceptor").val(),
                  cheMonto : $("#formAddChecks #cheMonto").val(),
                  cheFecha : $("#formAddChecks #cheFecha").val(),
                  cheConcepto : $("#formAddChecks #cheConcepto").val()
          };
      
      
          $.ajax({
              type: "POST",
              contentType: "application/json",
              url: "addCheck",
              data: JSON.stringify(ssiCheque),
              dataType: "json",
              beforeSend: function ( xhr ) {
                  console.log("before Send");
              },
              error: function (request, status, error) {            
                  console.log('Error ' /*+ request.responseText*/ + "\n" + status + "\n" + error);
              },
              success: function(data) {
                  console.log(data);
              }
          });
      

      控制器

      @RequestMapping(value = "/addCheck", method = RequestMethod.POST)
      @ResponseBody
      public SsiCheque addChecks(@Valid @RequestBody SsiCheque ssiCheque, BindingResult result) {
      
          //ssiCheque.persist();
          System.out.println("agregar " + result.getErrorCount());
          return ssiCheque;
      }
      

      谢谢!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-27
        • 2020-07-07
        • 2014-05-17
        • 2015-11-25
        • 2014-08-24
        • 2012-08-02
        • 2015-10-05
        相关资源
        最近更新 更多