SpringBoot项目实战之数据交互篇

 更新时间:2022年03月14日 11:44:51   作者:qchery  
这篇文章主要给大家介绍了关于SpringBoot项目实战之数据交互篇的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

SpringBoot 非常适合 Web 应用开发,我们可以使用它轻松地建立一个 Web 服务。在Spring Boot入门 里面,我们已经使用其实现一个非常简单的接口,输出了 Hello World!下面我们模拟真实的场景来学习 SpringBoot 应用开发。

1. 数据格式

在实际的项目场景中,前后分离几乎是所以项目的标配,全栈的时代的逐渐远去,后端负责业务逻辑处理,前端负责数据展示成了一种固定的开发模式。一般来说,在后端提供的数据接口中,可能会存在两种数据形式 Json 与 XML,具体会用到哪一种,往往会与公司的工程师文化相关。

1.1. Json 报文

对于 SpringBoot 来说 ,它默认会使用 Json 作为响应报文格式,我们用一个简单的例子做一下测试:

首先,我们创建一个 UserController 用于处理前端的 Web 请求。

@Controller
@RequestMapping("/sys/user")
public class UserController {

    @RequestMapping("login")
    @ResponseBody
    public Map<String, String> login() {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("msg", "登录成功");
        return hashMap;
    }

}

相信这个事例对于大多数使用过 SpringMVC 的人来说都不会陌生,定义一个简单的控制器及相当的映射,与通常返回 Url 的 Controller 不一样的是,我们这里使用了 @ResponseBody 注解,它表示此接口响应为纯数据,不带任何界面展示。请求对应的地址看到如下输出:

很明显,我们得到了想要的结果,一个标准格式的 Json 字符串,是不是非常的简单。

对于上面的代码来说,还可以做进一步的优化,由于所有的 Restful 接口都只是返回数据,所以我们可以直接在类级别上添加 @ResponseBody 注解。而大多数情况下,@Controller 与 @ResponseBody 又会一起使用,所以我们使用 @RestController 注解来替换掉它们,从而更加简洁地实现功能。

@RestController
@RequestMapping("/sys/user")
public class UserController {

    @RequestMapping("login")
    public Map<String, String> login() {
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("msg", "登录成功");
        return hashMap;
    }

}

1.2. Xml 报文

大多数情况下,使用 Json 就可以满足我们的需求了,但仍然存在某些特定的场景需要使用到 XML 形式的报文,如:微信公众号开发。不过不用担心,切换成 XML 报文也只需要做轻微的改动,添加相关依赖如下:

"com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.8.8"

然后就可以开始进行测试了,此处借助一个模拟 HTTP 请求工具(Postman)来协助我们测试该接口,使用方法就不多说了,直接上图:

在上面的测试范例里,我们指定了 Accept 为 text/xml,这样 SpringBoot 就会返回 XML 形式的数据。

2. 接口规范

对于每一家公司来说,都会定义自己的数据规范,一个统一且标准的数据规范对于系统维护来说是非常重要的,也在很在程度上提升了开发效率。

2.1. 响应报文规范

一般来说,接口响应至少需要告诉使用方三项信息:状态码、描述、数据。其中,数据不是每个接口必须的,如果只是一个简单修改的动作,可能就没有必须返回数据了。下面我们定义一个 Result 类来封装我们的响应报文。

public class Result {

    private int code;
    private String msg;
    private Object data;

    public Result(ResultCode resultCode, Object data) {
        this(resultCode);
        this.data = data;
    }

    public Result(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }

    ...
}

同时,定义一个枚举类来维护我们的状态码:

public enum  ResultCode {

    SUCCESS(0, "请求成功"),
    WARN(-1, "网络异常,请稍后重试");

    private int code;
    private String msg;

    ResultCode(int code, String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

}

这样,我们的响应数据规范已基本建立。将上面代码中返回 Map 修改为返回 Result,按照指定规范返回数据,得到结果如下。

2.2. 请求数据规范

响应报文格式我们已经定义好了,那么请求数据我们如何接收呢?一般来说,请求与响应会使用相同的报文形式,即,如果响应为Json,那么请求也建议使用Json。如果想要为上面的登录请求添加输入参数,需要完成以下几步:

首先,我们定义好用户实体:

public class User {

    private String username;
    private String password;

    ...
}

然后,直接在映射方法里面使用该实体进行参数接收,并将接收到的参数直接返回:

@RestController
@RequestMapping("/sys/user")
public class UserController {

    @RequestMapping("login")
    public Result login(@RequestBody User loginUser) {
        return new Result(ResultCode.SUCCESS, loginUser);
    }

}

调出我们的Postman,填写正确的Url,选择POST方式发送请求,选择Body,将 Content-Type 设置成 application/json,填入 Json 格式的请求数据,点击 Send 即可得到如下结果。

数据接收非常成功,但在上面的响应报文中,存在着了一个非常严重的问题,那就是用户的密码也随同用户信息一起返回给了客户端,显然这并不是一种正确的做法。我们需要对其进行一次过滤,由于 SpringBoot 默认使用 Jackson 作为 Json 序列化工具,如果想要过滤掉响应中的某些字段,只需在过滤字段对应的 get 方法上加上 @JsonIgnore 注解即可。但这样又会引发另外一个问题,那就是请求中的字段也被过滤掉了,对于这种问题,可以采用抽离请求参数模型的方式进行处理,即自定义一套参数接收的 Model,比如,接收用户登录的会使用 UserModel 来进行参数接收,这样使得请求参数模型与数据库映射实体完全分离,在一定程度上提升了系统的安全性。

@RequestMapping("login")
public Result login(@RequestBody UserModel userModel) {
    User user = new User();
    user.setUsername(userModel.getUsername());
    user.setPassword(userModel.getPassword());
    return new Result(ResultCode.SUCCESS, user);
}

替换成 Model 对象后,我们就可以在数据库映射实体 User 上增加 @JsonIgnore 注解忽略该字段的序列化,而不影响请求参数的输入。

3. 参数校验

出于系统健壮性的考虑,一般来说,我们需要对所有的参数进行必要性校验,如:登录请求时,如果没有用户名,程序应该立即驳回该请求。上面请求参数模型(Model)的抽象也使得我们对数据校验更加方便,当然主要还是依赖于 SpringBoot 的 Validate 功能的强大支持。

3.1. 简单参数校验

对于登录接口来说,用户名与密码都是必输的,那么我们现在为其添加上对应的参数校验,无需 if-else 判断,简单的几个注解就可以帮助我们完成所有的工作。

@RequestMapping("login")
public Result login(@RequestBody @Valid UserModel userModel) {
    ...
}
---
public class UserModel {

    private String username;
    private String password;

    @NotBlank(message = "用户名不能为空")
    public String getUsername() {
        return username;
    }

    @NotBlank(message = "密码不能为空")
    public String getPassword() {
        return password;
    }

    ...
}

在上面的示例中,我们为请求参数的 Model 对象加上了 @Valid 注解,并在 Model 类中,对需要校验字段的 get 方法上添加相应的校验注解。效果如下:

3.2. 复杂参数校验

3.2.1. 正则表达式校验

如果用户的登录名为手机号,那么就需要对登录名的格式做进一步的校验,下面使用正则表达式来校验手机号的合法性。

@NotBlank(message = "用户名不能为空")
@Pattern(
        regexp = "1(([38]\\d)|(5[^4&&\\d])|(4[579])|(7[0135678]))\\d{8}",
        message = "手机号格式不合法"
)
public String getUsername() {
    return username;
}

3.2.2. 自定义校验注解

在系统使用过程中,有很多地方需要对手机号的格式进行校验,如:注册、验证码发送等。但校验手机号的正则表达式又过于复杂,如果多处编写,一旦运营商增加某个号段,对程序的维护人员来说就是一个噩耗。这时,可以使用自定义校验注解来代替这些常用的校验。

手机号校验实现类 PhoneValidator:

public class PhoneValidator implements ConstraintValidator<Phone, String> {

    private Pattern pattern = Pattern.compile("1(([38]\\d)|(5[^4&&\\d])|(4[579])|(7[0135678]))\\d{8}");

    @Override
    public void initialize(Phone phone) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        return pattern.matcher(value).matches();
    }
}

手机号校验注解 Phone:

@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Phone {

    String message() default "手机号格式不合法";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

Model 上的使用:

@Phone
public String getUsername() {
    return username;
}

这样的话,如果因为某些不可抗拒因素导致校验规则的变动,只需要修改一处理即可,维护成本大大降低。

项目的 github 地址:https://github.com/qchery/funda

总结

到此这篇关于SpringBoot项目实战之数据交互篇的文章就介绍到这了,更多相关SpringBoot数据交互内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中FileWriter的用法及wirte()重载方法详解

    Java中FileWriter的用法及wirte()重载方法详解

    这篇文章主要介绍了Java中FileWriter的用法及wirte()重载方法详解,FileWriter是Java编程语言中的一个类,用于将字符写入文件,它提供了一种简单而方便的方式来创建、打开和写入文件,通过使用FileWriter,我们可以将字符数据写入文本文件,需要的朋友可以参考下
    2023-10-10
  • java生成随机数的常用方法分析

    java生成随机数的常用方法分析

    这篇文章主要介绍了java生成随机数的常用方法,结合实例形式分析了java生成随机数常用的方法功能与相关使用技巧,需要的朋友可以参考下
    2017-10-10
  • PowerJob LockService方法工作流程源码解读

    PowerJob LockService方法工作流程源码解读

    这篇文章主要为大家介绍了PowerJob LockService方法工作流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Java DriverManager.getConnection()获取数据库连接

    Java DriverManager.getConnection()获取数据库连接

    这篇文章主要介绍了Java DriverManager.getConnection()获取数据库连接,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Java 数据结构与算法系列精讲之单向链表

    Java 数据结构与算法系列精讲之单向链表

    单向链表特点是链表的链接方向是单向的,访问要通过顺序读取从头部开始。链表是使用指针构造的列表,是由一个个结点组装起来的,又称为结点列表。其中每个结点都有指针成员变量指向列表中的下一个结点,head指针指向第一个结点称为表头,而终止于最后一个指向nuLL的指针
    2022-02-02
  • 关于@PropertySource配置的用法解析

    关于@PropertySource配置的用法解析

    这篇文章主要介绍了关于@PropertySource配置的用法解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • postman测试传入List<String>参数方式

    postman测试传入List<String>参数方式

    这篇文章主要介绍了postman测试传入List<String>参数方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java HashMap算法原理详细讲解

    Java HashMap算法原理详细讲解

    在java开发中,HashMap是最常用、最常见的集合容器类之一,文中通过示例代码介绍HashMap为啥要二次Hash,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • JAVA面试题 start()和run()详解

    JAVA面试题 start()和run()详解

    这篇文章主要介绍了JAVA面试题 启动线程是start()还是run()?为什么?,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Java中Spring Boot+Socket实现与html页面的长连接实例详解

    Java中Spring Boot+Socket实现与html页面的长连接实例详解

    这篇文章主要介绍了Java中Spring Boot+Socket实现与html页面的长连接实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07

最新评论