首页 > 其他分享 >《优化接口设计的思路》系列:第一篇—接口参数的一些弯弯绕绕

《优化接口设计的思路》系列:第一篇—接口参数的一些弯弯绕绕

时间:2023-09-14 16:45:11浏览次数:45  
标签:param1 第一篇 接口 弯弯绕 参数 param2 ResponseEntity public String

大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。

接口参数是导致很多BUG产生的始作俑者,原因在于接口参数有3多:接口参数的取值地方多,如查询参数(Query Parameters)、路径参数(Path Parameters)、请求体(Request Body)等;数据类型多,如数字、字符、日期、文件等;判断情况多,如空值判断、格式判断、大小判断等;

一、接口参数的取值

1. 放在查询参数和请求体里

a、方法参数

示例代码如下:

@GetMapping("/testParams1")
public ResponseEntity<String> testParams1(String param1, Integer param2) {
  return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", param1, param2));
}

调用请求:http://localhost:8080/testParams1?param1=111&param2=222
返回如下:

没啥坑。

b、请求对象

GET请求

示例代码如下

@GetMapping("/testParams2")
public ResponseEntity<String> 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;
    }

    @Override
    public String toString() {
        return "ParamsReq{" +
            "param1='" + param1 + '\'' +
            ", param2='" + param2 + '\'' +
            '}';
    }
}

调用请求:http://localhost:8080/testParams2?param1=111&param2=222
返回如下:

这种有一个坑,Spring默认使用无参构造函数来实例化对象,所以ParamsReq不能是接口、抽象类等特殊类。

POST请求

示例代码如下:

@PostMapping("/testParams3")
public ResponseEntity<String> 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 ResponseEntity<String> testParams3(@RequestBody ParamsReq paramsReq)

2. 放在路径参数上

示例代码如下:

@GetMapping("/testParams4/{pathParam}")
public ResponseEntity<String> testParams4(@PathVariable("pathParam") String pathParam) {
  return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", pathParam));
}

参数使用{}框起来,然后使用@PathVariable即可获取到值,坑不多。

3. 放在请求头和Cookie

这两种情况里面的参数主要是标识类的参数如userToken,一般都是不变的,业务中很少使用到。

二、接口参数的类型

1. 数字、字符串

没啥坑。

2. 日期

示例代码如下:

@GetMapping("/testParams5")
public ResponseEntity<String> testParams5(Date date) {
  return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", date));
}

这里有个问题,这样的接口前端怎么传这个date值,字符串?时间戳?我已经替大家试过了,都不行,接口直接报400。

正确的做法是在日期参数前加上@DateTimeFormat注解,改成这样就行了:public ResponseEntity<String> testParams5(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date)
传参的话传字符串:2023-09-14 00:00:00 即可

3. 列表

示例代码如下:

@GetMapping("/testParams6")
public ResponseEntity<String> testParams6(List<Integer> paramList) {
  return ResponseEntity.ok(MessageFormat.format("paramList:[{0}];", paramList));
}

这串代码不用测试,它本身就是错误的,前面说过Spring默认使用无参构造函数来实例化对象,但是List是一个接口,没有无参构造函数。
为了解决这个问题,可以使用Spring的@RequestParam注解来指定参数名,并将多个参数值绑定到一个List对象中。

修改代码如下:public String testParams6(@RequestParam("paramList") List<Integer> paramList) 即可
然后,通过使用逗号分隔的参数值来访问接口,如:http://localhost:8080/testParams6?paramList=1,2,3
这样就可以成功传递参数列表并访问接口了。

4. 文件

先写一个简单上传界面
upload.html

<!DOCTYPE html>
<html>
<head>
    <title>File Upload Demo</title>
</head>
<body>
    <h1>File Upload Demo</h1>
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file" />
        <br/><br/>
        <input type="submit" value="Upload" />
    </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 ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        // Check if file is empty
        if (file.isEmpty()) {
            return new ResponseEntity<>("File is empty", HttpStatus.BAD_REQUEST);
        }

        // Save the file
        try {
            byte[] bytes = file.getBytes();
            // Logic to save the file to a desired location

            return 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引入

<!-- 接口参数校验 -->
<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>2.0.1.Final</version>
</dependency>

2. 注解分类

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;
    @PositiveOrZero
    private Double score;
}

这些东西看看就行了,用的时候翻一下文档就行,记也记不住。

四、一些可以直接获取到的参数

  • HttpServletRequest:用于获取HTTP请求的相关信息,包括请求头、请求参数、请求方法等。
  • HttpServletResponse:用于控制HTTP响应,包括设置响应状态码、设置响应头、发送响应内容等。
  • HttpSession:用于获取当前会话的信息,可以用来存储和获取会话级别的数据。
  • Principal:用于获取当前用户的身份信息,通常用于认证和授权。
  • Model/ModelMap:用于在请求处理方法中传递数据给前端视图。
  • BindingResult:用于获取请求参数绑定和验证的结果,包含了校验的错误信息。
  • Locale:用于获取当前请求的语言环境,可以用来进行国际化处理。
  • MultipartFile(或者List):用于处理上传的文件,包括文件的名称、大小、内容等。
  • RedirectAttributes:用于在重定向时传递数据给目标页面。
  • ServletRequest/ServletResponse:HttpServletRequest/HttpServletResponse的父类,可以使用其提供的通用方法。
  • @ModelAttribute注解:用于获取请求参数,并将其绑定到一个对象上。

这些对象可以直接在接口参数上使用,通过框架自动注入的方式获取其实例。在使用时,需要保证框架已经正确配置和启用了对应的注解和拦截器。用的最多的就是HttpServletRequest和HttpServletResponse了。

标签:param1,第一篇,接口,弯弯绕,参数,param2,ResponseEntity,public,String
From: https://www.cnblogs.com/wlovet/p/17700353.html

相关文章

  • 项目开发中难点-项目使用v-if控制表单/元素/组件显示隐藏,例如调用接口后赋值需重新加
    项目中使用v-if="show"  控制组件的显示或隐藏,当接口返回后this.show=false,进行赋值,后this.show=true显示 。但是页面没有正常显示,此时使用this.$nextTick。 一、$nextTick()概述1.$nextTick()原理$nextTick()是Vue.js框架中的一个方法,它主要用于DOM操作......
  • 接口慢
    接口慢异步思想,使用异步减少耗时操作。匹配联行号移到异步处理。2.空间换时间使用缓存缓存包括:Redis缓存,JVM本地缓存,memcached,或者Map等等。3.提前初始化到缓存中启动一个任务,提前把一些初始化信息缓存起来。4.池化思想,预分配和循环使用如果每次需要使用线程都去创建势必......
  • 中风失语 18 年,AI + 脑机接口帮她「意念发声」
    人与人交往中,说话表达是最基本的能力和方式,可世界上有很多人,却「有口难言」。「失语症」中,由中风引起的最为常见。他们的声音无法传达,他们的诉求不为人所知,他们遭受着社交孤立,他们的沉默震耳欲聋。每一个因中风而失语之人,无不渴望恢复完全、自然的交流能力,尽管目前全世界范围内......
  • 保证接口数据安全的10种方案
    前言我们日常开发中,如何保证接口数据的安全性呢?个人觉得,接口数据安全的保证过程,主要体现在这几个方面:一个就是数据传输过程中的安全,还有就是数据到达服务端,如何识别数据,最后一点就是数据存储的安全性。今天跟大家聊聊保证接口数据安全的10个方案。1.数据加密,防止报文明文传输。......
  • java调用第三方接口
    方法一:使用HttpURLConnection类调用第三方接口的示例代码:importjava.io.BufferedReader;importjava.io.InputStreamReader;importjava.net.HttpURLConnection;importjava.net.URL;publicclassThirdPartyApiCaller{publicstaticvoidmain(String[]args){......
  • 亚马逊API接口解析,实现按关键字搜索商品
    要解析亚马逊API接口并实现按关键字搜索商品,你需要按照以下步骤进行操作:了解亚马逊开发者中心:访问亚马逊开发者中心,并了解相关的API文档、开发者指南和规定。注册开发者账号:在亚马逊开发者中心上注册一个开发者账号,并创建一个应用,获取到API权限。获取API密钥:为了使用亚马逊API接......
  • Postman 接口测试:优化你的接口测试工作流程
    在现代软件开发生命周期中,接口测试是一个至关重要的部分。使用Postman这一工具,可以轻松地进行接口测试。以下是一份简单的使用教程,帮助你快速上手。安装Postman首先,你需要在电脑上安装Postman。你可以从官网上下载并安装它。安装完成后,你可以打开应用并创建一个新的请求。创建......
  • dubbo分布式项目开发____dubbo服务接口开发
    //1.创建服务提供者工程名 cnse-provider-user //2.导入jar包和创建工程包名//配置spring的xml文件<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-i......
  • 对SpringBoot接口进行操作日志记录
    最近业务有需求要对所有的用户操作进行日志记录,方便管理员查询不同权限级别的用户对系统的操作记录,现有的日志只是记录了异常信息、业务出错、重要功能的执行进行了记录,并不能满足需求要求,最直接的解决方法是在每个接口上去添加log.info之类的代码,但是这种方式对业务代码的切入性......
  • Ego微商小程序 - 接口测试
    接口测试设计流程1.需求分析与评审2.接口文档解析分析接口文档的核心内容API文档:Ego:Ego商城小程序,只存放项目前后端源码文件(gitee.com)3.设计测试用例与评审设计思路介绍一下你简历项目中的XXX项目的接口用例如何设计?答:下面是单接口-正向功能用例 ......