摆脱困境:向REST API添加验证

我对此感到有些to愧,但直到昨天,我才知道我可以使用@Valid@RequestBody批注将验证添加到REST API中。 这在Spring MVC 3.0中不起作用,由于某种原因,我没有注意到在Spring MVC 3.1中添加了对此功能的支持 。 我从不喜欢旧的方法,因为我不得不

  1. ValidatorMessageSource Bean注入到我的控制器中,以便我可以验证请求并在验证失败时获取本地化的错误消息。
  2. 在每个必须验证输入的控制器方法中调用验证方法。
  3. 将验证逻辑移到由控制器类扩展的公共基类中。

当我发现不再需要执行这些操作时,我决定写这篇博客文章,并与大家分享我的发现。

注意:如果我们想在Spring Framework中使用JSR-303支持的验证,我们必须向我们的类路径添加JSR-303提供程序。 本博客文章的示例应用程序使用Hibernate Validator 4.2.0,它是Bean验证API(JSR-303)的参考实现。

让我们首先看一下本博客文章中使用的DTO类。 CommentDTO类的源代码如下所示:

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;public class CommentDTO {@NotEmpty@Length(max = 140)private String text;//Methods are omitted.
}

让我们继续前进,了解如何使用Spring MVC 3.1向REST API添加验证。

Spring MVC 3.1是一个好的开始

我们可以按照以下步骤将验证添加到REST API:

  1. 实施控制器方法并确保其输入经过验证。
  2. 实现处理验证错误的逻辑。

以下小节将介绍这两个步骤。

实施控制器

我们可以通过执行以下步骤来实现我们的控制器:

  1. 创建一个名为CommentController的类,并使用@Controller注释对该类进行注释。
  2. 将一个add()方法添加CommentController类,该方法将添加的注释作为方法参数。
  3. 使用@RequestMapping和@ResponseBody注释对方法进行注释。
  4. @Valid@RequestBody批注应用到方法参数。
  5. 返回添加的评论。

CommentController类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;@Controller
public class CommentController {@RequestMapping(value = "/api/comment", method = RequestMethod.POST)@ResponseBodypublic CommentDTO add(@Valid @RequestBody CommentDTO comment) {return comment;}
}

现在,我们向控制器添加了新方法,并对其进行了验证。 验证失败时,将引发MethodArgumentNotValidException 。 让我们找出验证失败时如何向API用户返回有意义的响应。

处理验证错误

通过执行以下步骤,我们可以实现处理验证错误的逻辑:

  1. 实现数据传输对象,其中包含返回给我们REST API用户的信息。
  2. 实现异常处理程序方法。

下面将更详细地描述这些步骤。

创建数据传输对象

首先,我们必须创建数据传输对象,其中包含返回给REST API用户的信息。 我们可以按照以下步骤进行操作:

  1. 创建一个DTO,其中包含单个验证错误的信息。
  2. 创建将这些验证错误包装在一起的DTO。

让我们开始吧。

第一个DTO的源代码如下所示:

public class FieldErrorDTO {private String field;private String message;public FieldErrorDTO(String field, String message) {this.field = field;this.message = message;}//Getters are omitted.
}

第二个DTO的实现非常简单。 它包含一个FieldErrorDTO对象列表和一个用于向列表中添加新字段错误的方法。 ValidationErrorDTO的源代码如下所示:

import java.util.ArrayList;
import java.util.List;public class ValidationErrorDTO {private List<FieldErrorDTO> fieldErrors = new ArrayList<>();public ValidationErrorDTO() {}public void addFieldError(String path, String message) {FieldErrorDTO error = new FieldErrorDTO(path, message);fieldErrors.add(error);}//Getter is omitted.
}

以下清单提供了一个示例Json文档,当验证失败时,该文档将发送回我们的API用户:

{"fieldErrors":[{"field":"text","message":"error message"}]
}

让我们看看如何实现异常处理程序方法,该方法创建一个新的ValidationErrorDTO对象并返回创建的对象。

实现异常处理程序方法

我们可以按照以下步骤将异常处理程序方法添加到我们的控制器中:

  1. 将一个MessageSource字段添加到CommentController类。 消息源用于获取验证错误的本地化错误消息。
  2. 通过使用构造函数注入来注入MessageSource bean。
  3. 将一个processValidationError()方法添加到CommentController类。 此方法返回ValidationErrorDTO对象,并使用MethodArgumentNotValidException对象作为方法参数。
  4. 使用@ExceptionHandler注释对方法进行注释,并确保在引发MethodArgumentNotValidException时调用该方法。
  5. 使用@ResponseStatus注释对方法进行注释,并确保返回HTTP状态代码400(错误请求)。
  6. 使用@ResponseBody注释对方法进行注释。
  7. 实现该方法。

让我们仔细看看processValidationError()方法的实现。 我们可以通过执行以下步骤来实现此方法:

  1. 获取FieldError对象的列表并进行处理。
  2. 一次处理一场错误。
  3. 尝试使用MessageSource对象,当前语言环境和已处理字段错误的错误代码来解决本地化的错误消息。
  4. 返回已解决的错误消息。 如果从属性文件中找不到错误消息,请返回最准确的字段错误代码。
  5. 通过调用ValidationErrorDTO类的addFieldError()方法来添加新的字段错误。 将字段名称和已解决的错误消息作为方法参数传递。
  6. 处理完每个字段错误后,返回创建的ValidationErrorDTO对象。

CommentController类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;
import java.util.List;
import java.util.Locale;@Controller
public class CommentController {private MessageSource messageSource;@Autowiredpublic CommentController(MessageSource messageSource) {this.messageSource = messageSource;}//The add() method is omitted.@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBodypublic ValidationErrorDTO processValidationError(MethodArgumentNotValidException ex) {BindingResult result = ex.getBindingResult();List<FieldError> fieldErrors = result.getFieldErrors();return processFieldErrors(fieldErrors);}private ValidationErrorDTO processFieldErrors(List<FieldError> fieldErrors) {ValidationErrorDTO dto = new ValidationErrorDTO();for (FieldError fieldError: fieldErrors) {String localizedErrorMessage = resolveLocalizedErrorMessage(fieldError);dto.addFieldError(fieldError.getField(), localizedErrorMessage);}return dto;}private String resolveLocalizedErrorMessage(FieldError fieldError) {Locale currentLocale =  LocaleContextHolder.getLocale();String localizedErrorMessage = messageSource.getMessage(fieldError, currentLocale);//If the message was not found, return the most accurate field error code instead.//You can remove this check if you prefer to get the default error message.if (localizedErrorMessage.equals(fieldError.getDefaultMessage())) {String[] fieldErrorCodes = fieldError.getCodes();localizedErrorMessage = fieldErrorCodes[0];}return localizedErrorMessage;}
}

这就对了。 让我们花点时间评估我们刚刚完成的工作。

我们就快到了

现在,我们已经使用Spring MVC 3.1将验证添加到了REST API中。 与旧方法相比,此实现有一个主要优点:

我们可以使用@Valid批注来触​​发验证过程。

但是,仅当从包含异常处理程序方法的控制器类中抛出配置的异常时,才会触发使用@ExceptionHandler注释进行注释的方法。 这意味着,如果我们的应用程序具有多个控制器,则必须为控制器创建一个通用基类,并将处理验证错误的逻辑移至该类。 这听起来似乎没什么大不了,但是我们应该更喜欢组合而不是继承 。

Spring MVC 3.2提供了可用于从控制器中消除继承需求的工具。 让我们继续前进,找出实现方法。

Spring MVC 3.2抢救

Spring MVC 3.2引入了一个新的@ControllerAdvice批注 ,我们可以使用该批注实现一个异常处理程序组件,该组件处理控制器抛出的异常。 我们可以通过执行以下步骤来实现此组件:

  1. CommentController类中删除处理验证错误的逻辑。
  2. 创建一个新的异常处理程序类,并将处理验证错误的逻辑移至创建的类。

在以下小节中将更详细地说明这些步骤。

从我们的控制器中删除异常处理逻辑

我们可以按照以下步骤从控制器中删除异常处理逻辑:

  1. CommentController类中删除MessageSource字段。
  2. 从我们的控制器类中删除构造函数。
  3. 从我们的控制器类中删除processValidationError()方法和私有方法。

CommentController类的源代码如下所示:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;@Controller
public class CommentController {@RequestMapping(value = "/api/comment", method = RequestMethod.POST)@ResponseBodypublic CommentDTO add(@Valid @RequestBody CommentDTO comment) {return comment;}
}

我们的下一步是创建异常处理程序组件。 让我们看看这是如何完成的。

创建异常处理程序组件

我们可以按照以下步骤创建异常处理程序组件:

  1. 创建一个名为RestErrorHandler的类,并使用@ControllerAdvice批注对其进行批注。
  2. 将一个MessageSource字段添加到RestErrorHandler类。
  3. 通过使用构造函数注入来注入MessageSource bean。
  4. processValidationError()方法和所需的私有方法添加到RestErrorHandler类。

RestErrorHandler类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;import java.util.List;
import java.util.Locale;@ControllerAdvice
public class RestErrorHandler {private MessageSource messageSource;@Autowiredpublic RestErrorHandler(MessageSource messageSource) {this.messageSource = messageSource;}@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBodypublic ValidationErrorDTO processValidationError(MethodArgumentNotValidException ex) {BindingResult result = ex.getBindingResult();List<FieldError> fieldErrors = result.getFieldErrors();return processFieldErrors(fieldErrors);}private ValidationErrorDTO processFieldErrors(List<FieldError> fieldErrors) {ValidationErrorDTO dto = new ValidationErrorDTO();for (FieldError fieldError: fieldErrors) {String localizedErrorMessage = resolveLocalizedErrorMessage(fieldError);dto.addFieldError(fieldError.getField(), localizedErrorMessage);}return dto;}private String resolveLocalizedErrorMessage(FieldError fieldError) {Locale currentLocale =  LocaleContextHolder.getLocale();String localizedErrorMessage = messageSource.getMessage(fieldError, currentLocale);//If the message was not found, return the most accurate field error code instead.//You can remove this check if you prefer to get the default error message.if (localizedErrorMessage.equals(fieldError.getDefaultMessage())) {String[] fieldErrorCodes = fieldError.getCodes();localizedErrorMessage = fieldErrorCodes[0];}return localizedErrorMessage;}
}

我们终于到了

感谢Spring MVC 3.2,我们现在实现了一种优雅的解决方案,其中,验证由@Valid注释触发,并且异常处理逻辑移至单独的类。 我认为我们可以称之为一天,享受工作成果。

摘要

这篇博客文章告诉我们

  • 如果要在使用Spring 3.0时向REST API添加验证,则必须自己实现验证逻辑。
  • Spring 3.1通过使用@Valid批注将验证添加到REST API成为可能。 但是,我们必须创建一个包含异常处理逻辑的通用基类。 每个需要验证的控制器都必须扩展此基类。
  • 当使用Spring 3.2时,我们可以通过使用@Valid批注来触​​发验证过程,并将异常处理逻辑提取到单独的类中。

Github上提供了此博客的示例应用程序( Spring 3.1和Spring 3.2 )

参考资料: 从槽中汲取经验: Petri Kainulainen博客上的JCG合作伙伴 Petri Kainulainen 向REST API添加了验证 。

翻译自: https://www.javacodegeeks.com/2013/05/spring-from-the-trenches-adding-validation-to-a-rest-api.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

三星 原生android,原生Android 4.0 三星GALAXY Nexus评测

前言&#xff1a;北京时间10月19日上午10时&#xff0c;谷歌联合三星在香港举行了一场主题为“Calling all possibilities ”的新品发布会&#xff0c;在发布会上正式推出了搭载最新Android 4.0操作系统的智能手机三星GALAXY Nexus。三星GALAXY Nexus每一代的Nexus都被看做是An…

css类选择器类名覆盖优先级

code <style>.a{background: red;}.b{background: yellow;} </style> <div class"a b">A</div> 渲染效果 最初以为更改元素中class类里面的类名顺序&#xff0c;渲染效果就会根据类名顺序依次渲染code <style>.a{background: red;…

IntelliJ IDEA插件-常用插件

IntelliJ IDEA的插件真的很多&#xff0c;最近的新版集成的插件已经基本够用&#xff0c;下面是收集的一些常用插件&#xff0c;根据需要来安装和测试。如果还是没有找到&#xff0c;那么自己来开发一个。 官网&#xff1a;https://plugins.jetbrains.com/ 插件开发资料&#x…

CMD命令锦集

虽然随着计算机产业的发展&#xff0c;Windows 操作系统的应用越来越广泛&#xff0c;DOS 面临着被淘汰的命运&#xff0c;但是因为它运行安全、稳定&#xff0c;有的用户还在使用&#xff0c;所以一般Windows 的各种版本都与其兼容&#xff0c;用户可以在Windows 系统下运行DO…

在运行时访问工件的Maven和SCM版本

您可以轻松地告诉Maven在JAR清单文件中包含工件的版本及其Git / SVN /…修订版&#xff0c;然后在运行时通过getClass&#xff08;&#xff09;。getPackage访问该信息。 getImplementationVersion&#xff08;&#xff09; 。 &#xff08;所有功劳归功于MarkusKrger和其他同事…

Bootstrap学习笔记01

1、Make Images Mobile Responsive 用处&#xff1a; 使图片适配你的页面宽度。 操作&#xff1a; 给图片添加 .img-responsive class属性。 <img src"/images/cat.jpg" class"img-responsive"> 2、Center Text with Bootstrap 用处&#xff…

linux开机启动nginx

1.从创建文件nginx 位置/etc/init.d/nginx 2.粘贴内容 #!/bin/sh # # nginx - this script starts and stops the nginx daemon # # chkconfig: - 85 15 # description: Nginx is an HTTP(S) server, HTTP(S) reverse \ # proxy and IMAP/POP3 proxy server #…

您可能不知道的五个高级Java同步器

除了基于每个Java对象具有的锁定位的通用同步外&#xff0c;您还可以使用Java中更复杂的同步器&#xff0c;例如&#xff1a; 信号量 –使用许可的概念表示一个位置中允许的最大线程数。 当使用值1时&#xff0c;其行为类似于同步&#xff0c;也称为二进制信号量。 但是&#…

2017-2018-2 20179216 《网络攻防与实践》 SQL注入攻击

1. SQL语言 结构化查询语言(Structured Query Language)简称SQL&#xff1a;是一种特殊目的的编程语言&#xff0c;是一种数据库查询和程序设计语言&#xff0c;用于存取数据以及查询、更新和管理关系数据库系统&#xff1b;同时也是数据库脚本文件的扩展名。 2. SQL注入 SQL注…

借百度爸爸下蛋的好看搜索框

学习vue时写的一个小demo 虽然实现很简单&#xff0c;但还是推介看看&#xff0c;没什么难点&#xff0c;而且有些东西你可能会用到&#xff01;在线演示demo源码

数据库关系

第1层&#xff1a;高并发、高速查询 memory cached(内存高速缓存数据库)第2层&#xff1a;持久化保存 redis第3层&#xff1a;数据保存1、sql server,mysql,sqlite等关系型数据库2、mongo db 在很大程度上&#xff0c;redis可以替代mongo db&#xff08;持久化、文件保存&#…

web.config详解

<?xml version"1.0"?><!--注意: 除了手动编辑此文件以外&#xff0c;您还可以使用 Web 管理工具来配置应用程序的设置。可以使用 Visual Studio 中的“网站”->“Asp.Net 配置”选项。设置和注释的完整列表在 machine.config.comments 中&#xff0c;该…

华为智能手表与鸿蒙,不再是大号手环!华为鸿蒙手表来了:要和苹果抢生态?...

5 月 27 日上午&#xff0c;华为终端官方微博官宣&#xff0c;WATCH 3 腕上智慧&#xff0c;一表万象&#xff0c;旗舰归来&#xff0c;将于 6 月 2 日晚 20:00 与 HarmonyOS、MatePad Pro 2 等产品一同发布。依靠手环的超高销量&#xff0c;华为、小米等企业发力&#xff0c;在…

页面重构-让我们的布局自适应

css重构之旅 >前言&#xff1a; 今年我大一,马上就要大二了。从高三毕业暑假到大学的这一年马上过去&#xff0c;马上迎来大二生活.学习前端也有将近一年了。一昧去追求那些视觉的效果和相对高端和新颖的技术&#xff0c;反而忽略了最基础的布局技巧。 回味 2017年3月&am…

重新同步多线程集成测试

我最近在Captain Debug的Blog上偶然发现了一篇文章“ 同步多线程集成测试 ”。 该文章强调了设计涉及被测类以异步方式运行业务逻辑的集成测试的问题。 给出了这个人为的示例&#xff08;我删除了一些评论&#xff09;&#xff1a; public class ThreadWrapper {public void d…

[转] 分享一个快的飞起的maven的settings.xml文件

<?xml version"1.0"?> <settings> <localRepository>/home/yizhen/.m2/repository</localRepository><!--需要改成自己的maven的本地仓库地址--> <mirrors> <mirror> <id>alimaven</id> <name&g…

鸿蒙文化博物馆,有趣、有味、有文化!“周末儿童博物馆”在成博欢乐启幕

昨晚&#xff0c;成都博物馆“周末儿童博物馆”儿童节特别活动“六一欢乐会”拉开帷幕&#xff0c;丰富多彩的各种活动及精彩表演吸引了大批小朋友走进博物馆&#xff0c;提前度过了一个有趣、有味、有文化的“六一”国际儿童节。根据常设展“人与自然&#xff1a;贝林捐赠展”…

C8-2 圆的周长和面积 (100/100 分数)

题目描述 求圆的周长和面积&#xff0c;已知圆类从shape抽象类继承。 要求使用c class编写程序。可以创建如下class #include <iostream>using namespace std;const double pi 3.14;class Shape{public: Shape(){} ~Shape(){} virtual double getArea() 0; …

“前”方有坑,绕道而行(一)-- H5 CSS

1. 关于 数字、英文 不换行问题&#xff1b; 情景&#xff1a;昨天测试 小程序&#xff0c;输入英文&#xff0c;没有换行&#xff0c;且 下方有横向滚动条&#xff1b; 解决&#xff1a;word-break&#xff1b;  word-break:break-all; /*只对英文起作用&#xff0c;以字母…

Spring MVC:表单处理卷。 3 –复选框处理

我已经发布了有关使用Spring MVC标签库处理checkbox标签的帖子。 现在&#xff0c;我要开发此主题并继续使用“复选框”标签。 它并不难&#xff0c;但是在某些情况下&#xff0c;您最好使用它。 在本文中&#xff0c;我将结合java.util.List和java.util.Map提供Springcheckbox…