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

rest api

我对此感到有些ham愧,但是直到昨天,我才知道我可以通过使用@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

rest api

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

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

相关文章

2020-08-07 光纤通信第二章知识点整理

目录 2.1 半导体激光器 2 2.1.1 激光原理的基础知识 2 2.1.2 激光激射条件 3 2.1.3 结构理论 4 2.1.4 典型分类 6 2.1.5 模式概念 7 2&#xff0e;1.6 基本性质 8 2.2 半导体发光二极管 10 2.2.1 工作原理 10 2.2.2 结构和分类 10 2.2.3 主要性质 10 2.3 光源的直接调制 11 2.3…

静态分析的教育方面

加入我们项目的新程序员经常会问我们是否具有自动格式化工具&#xff0c;以使Java代码看起来完全符合Qulice的期望。 &#xff08;Quili是我们使用的静态分析器。&#xff09;我总是回答说&#xff0c;拥有这样一个自动代码抛光器只会有害&#xff0c;不会帮助项目及其成员改进…

2020-08-14 光纤通信第三章知识点整理

目录 3.1 光接收机 2 3.1.1 光接收机的分类与性能指标 2 3.1.2 直接检测光接收机的构成及功能 2 3.2 光电检测器 3 3.2.1 PN结的光电效应 4 3.2.2 PIN光电二极管 4 3.2.3 雪崩光电二极管&#xff08;APD&#xff09; 6 3.3 放大电路及其噪声 7 3.3.1 噪声的数学处理 8 3.3.2 放…

2020-08-21 光纤通信第四章知识点整理

第四章 光纤通信系统 目录 4.1 模拟光纤通信 2 4.2 数字光纤通信系统和总体设计 3 4.2.1 数字光纤通信系统基本组成 3 4.2.2 数字光纤通信系统总体设计 4 4.3 数字光纤传输系统的性能指标 7 4.3.1 误码性能 7 4.3.2 抖动、漂移性能 8 4.3.3 可用性 8 4.4 光纤放大器及其在光纤通…

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

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

2020-08-24 光纤通信第五章知识点整理

第五章 无源光器件和WDM技术 目录 5.1 无源器件的几个常用性能参数 1 5.2 光纤和波导型无源光器件 2 5.2.1 光连接器和光耦合器 2 5.2.2 偏振控制器 3 5.2.3 光纤布拉格光栅 4 5.2.4 Mach-Zahnder滤波器 5 5.2.5 非线性环路镜 5 5.3 光学无源器件 6 5.3.1 偏振分束器 6 5.3.2 光…

使用Java 9向Javadoc搜索添加术语

有一个相对较旧的网页&#xff0c;称为“ Proposed Javadoc Tags ”&#xff0c;最初似乎是与Javadoc 1.2一起编写的&#xff0c;其中列出了“ Sun有朝一日可能会在Javadoc中实现的标签”。 在此列表中的标签是category &#xff0c; example &#xff0c; tutorial &#xff0…

Java学习、简单代码编译

【2.1】求明天是星期几 public class study { public static void main(String args[]) { short i7; short tomorrow,yesterday; tomorrow (short) ((i1) % 7); yesterday (short)((i-17) % 7); System.out.print(“tomorrow:”tomorrow" “”\n"); System.out.pr…

Java学习:类的封装、继承和多态

【3.1】声明日期类及使用日期对象 public class MyDate{ //类声明 int year,month,day; //成员变量&#xff0c;表示年月日 void set(int y,int m,int d) //成员方法、设计日期值 { yeary; monthm; dayd; } void set(MyDate d) //将当前对象值设置为参数值&#xff0c;重载 { …

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

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

Java语言学习概述

md:创建新的文件夹 上面这行作用&#xff1a;将name&#xff1a;Tom,age12写入到文件1.doc中&#xff0c;也就是说先建个文档&#xff0c;再把这句话写进去。 del *.txt 作用&#xff1a;删除以txt结尾的所有文件 rd team2 作用&#xff1a;删除team2文件夹&#xff08;且这…

在CDI应用程序中使用@Alternative

在许多情况下&#xff0c;您可能希望拥有一个以上版本的Bean&#xff0c;并将其用于不同的目的。 备用bean的典型理由是出于测试目的&#xff0c;其中备用bean提供模拟数据。 为了获得实时数据&#xff0c;“真实” bean必须连接到的实时系统的好处是远程&#xff0c;或者在测试…

小狐狸ChatGPT付费创作系统 前端对话提示undefined index:temperature解决方法

很多会员使用着ChatGPT付费创作系统&#xff0c;特别是新安装系统后前端输入对话时提示错误undefined index:temperature&#xff0c;检查了反代、检查了KEY均正常&#xff0c;这问题主要是因为后台设置里新用户免费送对话次数为空&#xff0c;没设置赠送次数原因&#xff0c;导…

第二章 Java基本语法(一)

本文主要内容&#xff1a; 关键字、标识符、变量、类型转换、进制转换、运算符 一、关键字 二、标识符 比如&#xff1a;类名&#xff0c;变量名&#xff0c;方法名&#xff0c;接口名&#xff0c;包名。。。 小技巧&#xff1a;直接在我的电脑输入cmd&#xff0c;就可以打开…

第二章 Java基本语法(二)

本文主要内容&#xff1a; 运算符、if-else、switch-case 一、位运算符 运算符之五&#xff1a;位运算符 每左移一位&#xff0c;相当于是在原来的基础上乘2. int i 21; System.out.println(“i<<2:”(i<<2)); 输出&#xff1a;i<<2:84 结论&#xff1a; …

java 断点_每个Java开发人员应拥有的持久断点

java 断点当开发人员使用Java进行工作时&#xff0c;即使您尝试调试其他内容&#xff0c;也总是会遇到一些失败的情况。 这是应该在每个IDE中启用的持久断点的列表。 &#xff08;恕我直言&#xff09; 是的&#xff0c;从理论上讲&#xff0c;您应该能够从良好的日志记录实现…

第二章 Java基本语法(三)

本文主要内容&#xff1a; 循环结构、特殊关键字break和continue的使用&#xff0c;质数问题的优化 循环结构&#xff1a;Boolean类型 程序编写&#xff1a; 从键盘输入两个正整数m和n&#xff0c;找出他们的最大公约数和最小公倍数。 import java.util.Scanner; public clas…

用Priam设置Cassandra

前面已经解释了如何在AWS中设置Cassandra 。 所描述的设置有效&#xff0c;但在某些情况下可能还不够。 例如&#xff0c;它不能为您提供制作和还原备份的简便方法&#xff0c;添加新节点依赖于自定义python脚本&#xff0c;该脚本会随机选择一个种子。 因此&#xff0c;现在我…

Matlab入门(一)

MATLAB学习笔记 本系列主要内容来源&#xff1a;matlab官方付费课程的学习笔记 截图较多&#xff0c;之后更新会尽量增加文字说明 matlab简单常用的命令 数组 round:四舍五入函数。

MATLAB入门(二)

可以使用 plot 函数在一张图上绘制两个相同长度的向量。 plot(x,y)plot 函数接受一个附加参数。使用该参数&#xff0c;您可以通过在引号中包含不同符号的方式来指定与之对应的颜色、线型和标记样式。 plot(x,y,"r--o")以上命令将会绘制一条红色 虚线 (–)&#x…