如何在 Spring 4 MVC 中使用 javax.validation 和 JSON 请求?
How to use javax.validation and JSON request in Spring 4 MVC?
我正在使用 Spring 4 MVC 开发 Web 应用程序。我想知道我是否可以 validate JSON 使用 javax.validation API 请求对象。例如,我有我的 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。我所看到的只是使用 自定义验证器 和 "rewrite" 验证代码。
这是验证 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 对象格式和控制器规格不兼容时遇到过这个错误,但这次我不知道为什么会出错。
再次感谢!
这里似乎有一些问题:
您的 object 结构看起来很奇怪。为什么您的字段引用 object 类型? private Date SsiCheque.cheFecha
似乎完全是 non-sensical 字段。
您通常将 UI 设计为通过可以直接映射到您的 Java object 的 JSON object 发送.因此,如果您的 object 看起来像这样:
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 可以使用它直接映射到您的 object。
然后假设您的项目类路径中包含 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;
}
}
重要的部分是你的 object 是请求 body 并且你使用 @RequestBody
注释,因为杰克逊使用它作为构建你的 object 的信号使用 HTTP 请求 Body 中的 JSON。此方法的唯一缺点是您可能必须以编程方式构建您的请求 JSON。然而,这对于 JavaScript 来说是微不足道的。
(我将在这里假设一些合理的输入 ID 默认值,并且您熟悉 jQuery DOM manipulation/selection 语法)
var bazArray = [];
$.forEach($("#bazContainer"), function (baz, i){
bazArray.push(baz);
});
var example = {
foo: $("#fooInput").val(),
bar: $("#barInput").val(),
baz: bazArray
};
你把你的例子object传递给你在数据字段中的请求,如果你指定它是application/json
类型那么jQuery会自动调用JSON.stringify
在你的例子中 object。
希望这一切都有意义。
SOLUTION(提问者更新:Jessai)
我检查了这个问题:Spring MVC 400 Bad Request Ajax。
总结一下我做了什么:
创建一个要解析的对象JSON.stringify并发送给控制器
在控制器中,我使用 @ResponseBody 和 @RequestBody 设置方法,正如@James Massey 所说。
在实体中,我向字段添加了@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;
}
谢谢!
我已经用另一种方式解决了我的验证问题。假设我有代理对象:
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>
现在看看特工 class 的样子:
@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") - 解决 name 是强制性的,你有如何指定错误消息的例子
(3) 我定义的@CheckBools 注解,在 class 级别,它检查 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 {};
}
在下面class你定义规则
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
我正在使用 Spring 4 MVC 开发 Web 应用程序。我想知道我是否可以 validate JSON 使用 javax.validation API 请求对象。例如,我有我的 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。我所看到的只是使用 自定义验证器 和 "rewrite" 验证代码。
这是验证 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 对象格式和控制器规格不兼容时遇到过这个错误,但这次我不知道为什么会出错。
再次感谢!
这里似乎有一些问题:
您的 object 结构看起来很奇怪。为什么您的字段引用 object 类型?
private Date SsiCheque.cheFecha
似乎完全是 non-sensical 字段。您通常将 UI 设计为通过可以直接映射到您的 Java object 的 JSON object 发送.因此,如果您的 object 看起来像这样:
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 可以使用它直接映射到您的 object。
然后假设您的项目类路径中包含 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;
}
}
重要的部分是你的 object 是请求 body 并且你使用 @RequestBody
注释,因为杰克逊使用它作为构建你的 object 的信号使用 HTTP 请求 Body 中的 JSON。此方法的唯一缺点是您可能必须以编程方式构建您的请求 JSON。然而,这对于 JavaScript 来说是微不足道的。
(我将在这里假设一些合理的输入 ID 默认值,并且您熟悉 jQuery DOM manipulation/selection 语法)
var bazArray = [];
$.forEach($("#bazContainer"), function (baz, i){
bazArray.push(baz);
});
var example = {
foo: $("#fooInput").val(),
bar: $("#barInput").val(),
baz: bazArray
};
你把你的例子object传递给你在数据字段中的请求,如果你指定它是application/json
类型那么jQuery会自动调用JSON.stringify
在你的例子中 object。
希望这一切都有意义。
SOLUTION(提问者更新:Jessai)
我检查了这个问题:Spring MVC 400 Bad Request Ajax。
总结一下我做了什么:
创建一个要解析的对象JSON.stringify并发送给控制器
在控制器中,我使用 @ResponseBody 和 @RequestBody 设置方法,正如@James Massey 所说。
在实体中,我向字段添加了@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;
}
谢谢!
我已经用另一种方式解决了我的验证问题。假设我有代理对象:
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>
现在看看特工 class 的样子:
@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") - 解决 name 是强制性的,你有如何指定错误消息的例子 (3) 我定义的@CheckBools 注解,在 class 级别,它检查 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 {};
}
在下面class你定义规则
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