- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教.
作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧.
接口参数是导致很多BUG产生的始作俑者,原因在于接口参数有3多:接口参数的取值地方多,如查询参数(Query Parameters)、路径参数(Path Parameters)、请求体(Request Body)等;数据类型多,如数字、字符、日期、文件等;判断情况多,如空值判断、格式判断、大小判断等; 。
示例代码如下:
@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¶m2=222 返回如下:
没啥坑.
示例代码如下 。
@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¶m2=222 返回如下:
这种有一个坑,Spring默认使用无参构造函数来实例化对象,所以ParamsReq不能是接口、抽象类等特殊类.
示例代码如下:
@PostMapping("/testParams3")
public ResponseEntity<String> testParams3(ParamsReq paramsReq) {
return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", paramsReq.getParam1(), paramsReq.getParam2()));
}
ParamsReq类代码同上 。
和GET请求类似,没啥坑.
没啥坑.
这里有坑了,当content-type为application/json时,接口参数取值为空。这时就需要在参数前加上一个注解: @RequestBody ,原因是通过 @RequestBody 注解,Spring Boot可以自动地将请求体中的JSON数据转换为Java对象,从而方便地进行数据的处理和转换。如果不加 @RequestBody 注解,Spring Boot默认会将请求体中的JSON数据作为普通的表单数据来处理,而不会自动转换为Java对象。:
改成这样就行: public ResponseEntity<String> testParams3(@RequestBody ParamsReq paramsReq) 。
示例代码如下:
@GetMapping("/testParams4/{pathParam}")
public ResponseEntity<String> testParams4(@PathVariable("pathParam") String pathParam) {
return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", pathParam));
}
参数使用 { 和 } 框起来,然后使用 @PathVariable 即可获取到值,坑不多.
这两种情况里面的参数主要是标识类的参数如userToken,一般都是不变的,业务中很少使用到.
没啥坑.
示例代码如下
@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 即可 。
示例代码如下
@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 这样就可以成功传递参数列表并访问接口了.
先写一个简单上传界面 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对象,而校验参数值是否合法通常有下面几种做法:
@NotNull
、 @NotBlank
这里主要讲一下javax.validation如何使用! 。
<!-- 接口参数校验 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
注解 | 说明 | 使用频率 |
---|---|---|
@NotNull | 不能为null,常用于数字、日期 | 常用 |
@NotBlank | 不能为null也不能为空,常用于字符串 | 常用 |
@NotEmpty | 集合不能为空,常用于List、Map、Set | 常用 |
注解 | 说明 | 使用频率 |
---|---|---|
@Max | 被注释的元素必须小于等于指定的值 | 常用 |
@Min | 被注释的元素必须大于等于指定的值 | 常用 |
@Positive | 被注释的元素必须是正数 | 不常用 |
@Negative | 被注释的元素必须是负数 | 不常用 |
注解 | 说明 | 使用频率 |
---|---|---|
@AssertFalse | 被注释的元素必须是false | 常用 |
@AssertTrue | 被注释的元素必须是true | 常用 |
注解 | 说明 | 使用频率 |
---|---|---|
@Future | 被注释的元素必须是将来的日期 | 不常用 |
@Past | 被注释的元素必须是过去的日期 | 不常用 |
注解 | 说明 | 使用频率 |
---|---|---|
被注释的元素必须是电子邮箱地址 | 常用 | |
@Pattern | 被注释的元素必须是符合正则表达式,我经常使用这个判断手机号是否合法 | 常用 |
下面是一个经典的案例 。
@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和HttpServletResponse了.
作者: 不若为止 欢迎任何形式的转载,但请务必注明出处。 限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教.
最后此篇关于《优化接口设计的思路》系列:第一篇—接口参数的一些弯弯绕绕的文章就讲到这里了,如果你想了解更多关于《优化接口设计的思路》系列:第一篇—接口参数的一些弯弯绕绕的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
简而言之:我想从可变参数模板参数中提取各种选项,但不仅通过标签而且通过那些参数的索引,这些参数是未知的 标签。我喜欢 boost 中的方法(例如 heap 或 lockfree 策略),但想让它与 S
我可以对单元格中的 excel IF 语句提供一些帮助吗? 它在做什么? 对“BaselineAmount”进行了哪些评估? =IF(BaselineAmount, (Variance/Baselin
我正在使用以下方法: public async Task Save(Foo foo,out int param) { ....... MySqlParameter prmparamID
我正在使用 CodeGear RAD Studio IDE。 为了使用命令行参数测试我的应用程序,我多次使用了“运行 -> 参数”菜单中的“参数”字段。 但是每次我给它提供一个新值时,它都无法从“下拉
我已经为信用卡类编写了一些代码,粘贴在下面。我有一个接受上述变量的构造函数,并且正在研究一些方法将这些变量格式化为字符串,以便最终输出将类似于 号码:1234 5678 9012 3456 截止日期:
MySql IN 参数 - 在存储过程中使用时,VarChar IN 参数 val 是否需要单引号? 我已经像平常一样创建了经典 ASP 代码,但我没有更新该列。 我需要引用 VarChar 参数吗?
给出了下面的开始,但似乎不知道如何完成它。本质上,如果我调用 myTest([one, Two, Three], 2); 它应该返回元素 third。必须使用for循环来找到我的解决方案。 funct
将 1113355579999 作为参数传递时,该值在函数内部变为 959050335。 调用(main.c): printf("%d\n", FindCommonDigit(111335557999
这个问题在这里已经有了答案: Is Java "pass-by-reference" or "pass-by-value"? (92 个回答) 关闭9年前。 public class StackOve
我真的很困惑,当像 1 == scanf("%lg", &entry) 交换为 scanf("%lg", &entry) == 1 没有区别。我的实验书上说的是前者,而我觉得后者是可以理解的。 1 =
我正在尝试使用调用 SetupDiGetDeviceRegistryProperty 的函数使用德尔福 7。该调用来自示例函数 SetupEnumAvailableComPorts .它看起来像这样:
我需要在现有项目上实现一些事件的显示。我无法更改数据库结构。 在我的 Controller 中,我(从 ajax 请求)传递了一个时间戳,并且我需要显示之前的 8 个事件。因此,如果时间戳是(转换后)
rails 新手。按照多态关联的教程,我遇到了这个以在create 和destroy 中设置@client。 @client = Client.find(params[:client_id] || p
通过将 VM 参数设置为 -Xmx1024m,我能够通过 Eclipse 运行 Java 程序-Xms256M。现在我想通过 Windows 中的 .bat 文件运行相同的 Java 程序 (jar)
我有一个 Delphi DLL,它在被 Delphi 应用程序调用时工作并导出声明为的方法: Procedure ProduceOutput(request,inputs:widestring; va
浏览完文档和示例后,我还没有弄清楚 schema.yaml 文件中的参数到底用在哪里。 在此处使用 AWS 代码示例:https://github.com/aws-samples/aws-proton
程序参数: procedure get_user_profile ( i_attuid in ras_user.attuid%type, i_data_group in data_g
我有一个字符串作为参数传递给我的存储过程。 dim AgentString as String = " 'test1', 'test2', 'test3' " 我想在 IN 中使用该参数声明。 AND
这个问题已经有答案了: When should I use "this" in a class? (17 个回答) 已关闭 6 年前。 我运行了一些java代码,我看到了一些我不太明白的东西。为什么下
我输入 scroll(0,10,200,10);但是当它运行时,它会传递字符串“xxpos”或“yypos”,我确实在没有撇号的情况下尝试过,但它就是行不通。 scroll = function(xp
我是一名优秀的程序员,十分优秀!