深圳网站定制开发,安徽建设人才网官网,上市公司网站维护,星斗科技 网站建设系列文章导航 第一篇—接口参数的一些弯弯绕绕 第二篇—接口用户上下文的设计与实现 第三篇—留下用户调用接口的痕迹 第四篇—接口的权限控制 第五篇—接口发生异常如何统一处理
本文参考项目源码地址#xff1a;summo-springboot-interface-demo
前言
大家好#xff01;…系列文章导航 第一篇—接口参数的一些弯弯绕绕 第二篇—接口用户上下文的设计与实现 第三篇—留下用户调用接口的痕迹 第四篇—接口的权限控制 第五篇—接口发生异常如何统一处理
本文参考项目源码地址summo-springboot-interface-demo
前言
大家好我是sum墨一个一线的底层码农平时喜欢研究和思考一些技术相关的问题并整理成文限于本人水平如果文章和代码有表述不当之处还请不吝赐教。
作为一名从业已达六年的老码农我的工作主要是开发后端Java业务系统包括各种管理后台和小程序等。在这些项目中我设计过单/多租户体系系统对接过许多开放平台也搞过消息中心这类较为复杂的应用但幸运的是我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点一是业务系统本身并不复杂二是我一直遵循某大厂代码规约在开发过程中尽可能按规约编写代码三是经过多年的开发经验积累我成为了一名熟练工掌握了一些实用的技巧。
接口参数是导致很多BUG产生的始作俑者原因在于接口参数有3多接口参数的取值地方多如查询参数Query Parameters、路径参数Path Parameters、请求体Request Body等数据类型多如数字、字符、日期、文件等判断情况多如空值判断、格式判断、大小判断等
一、接口参数的取值
1. 放在查询参数和请求体里
a、方法参数
示例代码如下
GetMapping(/testParams1)
public ResponseEntityString testParams1(String param1, Integer param2) {return ResponseEntity.ok(MessageFormat.format(param1:[{0}];param2:[{1}], param1, param2));
}调用请求http://localhost:8080/testParams1?param1111param2222 返回如下 没啥坑。 b、请求对象
GET请求
示例代码如下
GetMapping(/testParams2)
public ResponseEntityString testParams2(ParamsReq paramsReq) {return ResponseEntity.ok(MessageFormat.format(param1:[{0}];param2:[{1}], paramsReq.getParam1(), paramsReq.getParam2()));
}ParamsReq.java
public class ParamsReq {private String param1;private String param2;public ParamsReq() {}public ParamsReq(String param1, String param2) {this.param1 param1;this.param2 param2;}public String getParam1() {return param1;}public void setParam1(String param1) {this.param1 param1;}public String getParam2() {return param2;}public void setParam2(String param2) {this.param2 param2;}Overridepublic String toString() {return ParamsReq{ param1 param1 \ , param2 param2 \ };}
}
调用请求http://localhost:8080/testParams2?param1111param2222 返回如下 这种有一个坑Spring默认使用无参构造函数来实例化对象所以ParamsReq不能是接口、抽象类等特殊类。 POST请求
示例代码如下
PostMapping(/testParams3)
public ResponseEntityString testParams3(ParamsReq paramsReq) {return ResponseEntity.ok(MessageFormat.format(param1:[{0}];param2:[{1}], paramsReq.getParam1(), paramsReq.getParam2()));
}ParamsReq类代码同上
调用方式1参数放在链接上 和GET请求类似没啥坑。 调用方式2放在Form表单中content-type为application/x-www-form-urlencoded 没啥坑。 调用方式3放在body参数中content-type为application/json 这里有坑了当content-type为application/json时接口参数取值为空。这时就需要在参数前加上一个注解RequestBody原因是通过RequestBody注解Spring Boot可以自动地将请求体中的JSON数据转换为Java对象从而方便地进行数据的处理和转换。如果不加RequestBody注解Spring Boot默认会将请求体中的JSON数据作为普通的表单数据来处理而不会自动转换为Java对象。 改成这样就行public ResponseEntityString testParams3(RequestBody ParamsReq paramsReq) 2. 放在路径参数上
示例代码如下
GetMapping(/testParams4/{pathParam})
public ResponseEntityString testParams4(PathVariable(pathParam) String pathParam) {return ResponseEntity.ok(MessageFormat.format(pathParam:[{0}];, pathParam));
}参数使用{和}框起来然后使用PathVariable即可获取到值坑不多。 3. 放在请求头和Cookie 这两种情况里面的参数主要是标识类的参数如userToken一般都是不变的业务中很少使用到。 二、接口参数的类型
1. 数字、字符串 没啥坑。 2. 日期
示例代码如下:
GetMapping(/testParams5)
public ResponseEntityString testParams5(Date date) {return ResponseEntity.ok(MessageFormat.format(pathParam:[{0}];, date));
}这里有个问题这样的接口前端怎么传这个date值字符串时间戳我已经替大家试过了都不行接口直接报400。 正确的做法是在日期参数前加上DateTimeFormat注解改成这样就行了public ResponseEntityString testParams5(DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss) Date date)。 传参的话传字符串2023-09-14 00:00:00 即可 3. 列表
示例代码如下:
GetMapping(/testParams6)
public ResponseEntityString testParams6(ListInteger paramList) {return ResponseEntity.ok(MessageFormat.format(paramList:[{0}];, paramList));
}这串代码不用测试它本身就是错误的前面说过Spring默认使用无参构造函数来实例化对象但是List是一个接口没有无参构造函数。 为了解决这个问题可以使用Spring的RequestParam注解来指定参数名并将多个参数值绑定到一个List对象中。 修改代码如下public String testParams6(RequestParam(paramList) ListInteger paramList) 即可 然后通过使用逗号分隔的参数值来访问接口如http://localhost:8080/testParams6?paramList1,2,3 这样就可以成功传递参数列表并访问接口了。 4. 文件
先写一个简单上传界面 upload.html
!DOCTYPE html
html
headtitleFile Upload Demo/title
/head
bodyh1File Upload Demo/h1form actionhttp://localhost:8080/upload methodpost enctypemultipart/form-datainput typefile namefile /br/br/input typesubmit valueUpload //form
/body
/html后端上传代码 FileUploadController.java
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;Controller
public class FileUploadController {PostMapping(/upload)public ResponseEntityString uploadFile(RequestParam(file) MultipartFile file) {// Check if file is emptyif (file.isEmpty()) {return new ResponseEntity(File is empty, HttpStatus.BAD_REQUEST);}// Save the filetry {byte[] bytes file.getBytes();// Logic to save the file to a desired locationreturn new ResponseEntity(File uploaded successfully, HttpStatus.OK);} catch (Exception e) {return new ResponseEntity(Failed to upload file, HttpStatus.INTERNAL_SERVER_ERROR);}}
}上传接口后端其实还好主要是前端需要处理的内容多一些由于MultipartFile类也是一个接口所以这里也需要加上RequestParam注解。 三、接口参数的判断
前面提到的RequestBody、RequestParam注解都是SpringBoot自带的它们主要的功能是将请求参数转换为我们接口定义的变量或者Java对象而校验参数值是否合法通常有下面几种做法
自己写校验逻辑一般是配合使用Assert进行参数校验使用javax.validation包的校验注解如NotNull、NotBlank
这里主要讲一下javax.validation如何使用
1. pom.xml引入
!-- 接口参数校验 --
dependencygroupIdjavax.validation/groupIdartifactIdvalidation-api/artifactIdversion2.0.1.Final/version
/dependency2. 注解分类
a. 空值检查
注解说明使用频率NotNull不能为null常用于数字、日期常用NotBlank不能为null也不能为空常用于字符串常用NotEmpty集合不能为空常用于List、Map、Set常用
b. 数值检查
注解说明使用频率Max被注释的元素必须小于等于指定的值常用Min被注释的元素必须大于等于指定的值常用Positive被注释的元素必须是正数不常用Negative被注释的元素必须是负数不常用
c. Boolean 检查
注解说明使用频率AssertFalse被注释的元素必须是false常用AssertTrue被注释的元素必须是true常用
d. 日期检查
注解说明使用频率Future被注释的元素必须是将来的日期不常用Past被注释的元素必须是过去的日期不常用
e. 日期检查
注解说明使用频率Email被注释的元素必须是电子邮箱地址常用Pattern被注释的元素必须是符合正则表达式我经常使用这个判断手机号是否合法常用
3. 使用方法
下面是一个经典的案例
Data
public class StudentReq {NotBlank(message 主键不能为空)private String id;NotBlank(message 名字不能为空)Size(min 2, max 4, message 名字字符长度必须为 2~4个)private String name;Pattern(regexp ^1[3456789]\\d{9}$, message 手机号格式错误)private String phone;Email(message 邮箱格式错误)private String email;Past(message 生日必须早于当前时间)private Date birth;Min(value 0, message 年龄必须为 0~100)Max(value 100, message 年龄必须为 0~100)private Integer age;PositiveOrZeroprivate Double score;
}这些东西看看就行了用的时候翻一下文档就行记也记不住。 四、一些可以直接获取到的参数
HttpServletRequest用于获取HTTP请求的相关信息包括请求头、请求参数、请求方法等。HttpServletResponse用于控制HTTP响应包括设置响应状态码、设置响应头、发送响应内容等。HttpSession用于获取当前会话的信息可以用来存储和获取会话级别的数据。Principal用于获取当前用户的身份信息通常用于认证和授权。Model/ModelMap用于在请求处理方法中传递数据给前端视图。BindingResult用于获取请求参数绑定和验证的结果包含了校验的错误信息。Locale用于获取当前请求的语言环境可以用来进行国际化处理。MultipartFile或者List用于处理上传的文件包括文件的名称、大小、内容等。RedirectAttributes用于在重定向时传递数据给目标页面。ServletRequest/ServletResponseHttpServletRequest/HttpServletResponse的父类可以使用其提供的通用方法。ModelAttribute注解用于获取请求参数并将其绑定到一个对象上。
这些对象可以直接在接口参数上使用通过框架自动注入的方式获取其实例。在使用时需要保证框架已经正确配置和启用了对应的注解和拦截器。用的最多的就是HttpServletRequest和HttpServletResponse了。