使用idea集成的springboot实现注册接口

跟黑马程序员学pringboot3+vue3,代码都是黑马程序员视频里的代码

实战篇-03_注册接口_哔哩哔哩_bilibili

本文仅仅用于学习记录

开发用户接口

注册接口

开发流程:明确需求>>阅读接口文档>>思路分析>>开发>>测试

分析:

这个图是截黑马ppt的图:

问题(今天在注册这个接口这里都搞不定!!!遇到很多问题!!!)

问题1:

因为有写依赖与springboot项目依赖版本号不兼容

快速的解决方法:

直接新建springboot项目,并且勾选这些需要用到的依赖,就是自动生成兼容的依赖,复制生成的pom.xml文件替换原来的pom.xml文件,修改一下pom的名字即可。

所以本菜鸟更喜欢一开始就建立springboot项目,哭了。。。。。。。

新建项目

创建实体类

首先先在启动类的包下创建以下几个包:(项目结构)

controller包

service包

(service包下再新建一个Impl包)

mapper包

pojo包

utils包

根据数据库表结构,在 pojo 包下创建 User.java 实体类

java 复制代码
package com.example.demo.pojo;



import lombok.Data;

import java.time.LocalDateTime;

@Data
public class User {
    private Integer id;//主键ID
    private String username;//用户名
    private String password;//密码
    private String nickname;//昵称
    private String email;//邮箱
    private String userPic;//用户头像地址
    private LocalDateTime createTime;//创建时间
    private LocalDateTime updateTime;//更新时间
}

自动给实体类生成Geeter和Setter方法

  1. 在pom.xml中添加lombok依赖

2.在实体类中添加@Data

在application.yml文件配置配置数据库连接信息

废话来了:我们开发的是注册接口,需要往数据库里添加用户数据,所以一开始就把数据库连接信息配置好

路径:src/main/resources/application.properties

先把application.properties文件改造为application.yml

(如何修改 ,这里有:配置文件application.properties-CSDN博客中的yml部分)

配置数据库信息:

java 复制代码
server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/big_event
    username: root
    password: 123456

查看接口文档,了解注册接口post请求长这样

http://localhost:8080/user/register?username=user1\&password=123456

创建控制器

src/main/java/com/example/demo/controller 包下创建 UserController.java 控制器。

定义注册接口 /register 并实现注册逻辑

java 复制代码
package com.example.demo.controller;


import com.example.demo.pojo.Result;
import com.example.demo.pojo.User;
import com.example.demo.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/*
@Resource注解用于自动注入依赖的 Bean。它是一个泛型注解,可以自动按类型注入,
也可以通过指定 name 属性来注入特定的 Bean。
*/

@Resource

/*@RestController注解用于将类标记为控制器,主要用来处理 HTTP 请求。
它相当于 @Controller 和 @ResponseBody 注解的组合,表示类中的方法返回的数据直接写入 HTTP 响应体中,而不是返回一个视图名称。
*/

@RestController

/*
@RequestMapping("/user")注解用于映射 HTTP 请求到控制器的处理方法。
这里指定了所有以 "/user" 开头的请求都会被这个控制器处理。
*/
@RequestMapping("/user")


public class UserController {

/*
@Autowired注解也是用于自动注入依赖的 Bean,
通常用于字段、构造函数、设置方法和普通方法参数。
它通过类型来自动注入 Bean,如果存在多个同类型的 Bean,
则需要通过 @Qualifier 注解指定具体的 Bean 名称
*/
    @Autowired
    private UserService userService;

/*
@PostMapping("/register")注解用于映射 HTTP POST 请求到特定的处理方法。
这里指定了当接收到 "/user/register" 的 POST 请求时,会调用 register 方法
*/
    @PostMapping("/register")
    public Result register(String username, String password){
        //查询用户名是否已存在
        User user =userService.findByUserName(username);
        if(user == null){
            userService.register(username,password);
            return Result.success();
        }else{
            //占用
            return Result.error("用户名已经被占用");
        }
    }



}

"不知道Bean注入的,看一下Bean部分的内容"

创建服务层接口和实现(实验查询用户是否存在和注册用户的业务逻辑)

src/main/java/com/example/demo/service 包下创建 UserService.java 接口

java 复制代码
package com.example.demo.service;


import com.example.demo.pojo.User;

public interface UserService {
    // 根据用户名查询用户
     User findByUserName(String username) ;
//注册
     void register(String username, String password) ;
}

src/main/java/com/example/demo/service /Impl包下创建 UserServiceImpl.java 实现 UserService 接口

java 复制代码
package com.example.demo.service.impl;


import com.example.demo.mapper.UserMapper;
import com.example.demo.pojo.User;
import com.example.demo.service.UserService;
import com.example.demo.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/*
@Service注解用于标记服务层的组件,它指示 Spring 容器将这个类作为一个 Bean 进行管理。
通常用于业务逻辑层,但不限于此
*/
@Service
public class UserServiceImpl implements UserService {

/*
@Autowired注解用于自动注入依赖的 Bean。
在这个例子中,它被用来自动注入 UserMapper 类的实例,
这是数据访问层的一个组件
*/
    @Autowired
    private UserMapper usermapper;
    @Override
    public User findByUserName(String username){
       User user = usermapper.findByUserName(username);
       return user;
    }
    @Override
    public void register(String username, String password){
        //加密
       String md5String= Md5Util.getMD5String(password);
       //添加
        usermapper.add(username,md5String);

    }
}

创建 MyBatis Mapper 接口(与数据库交互,进行数据库操作就写在这里)

src/main/java/com/example/demo/mapper 包下创建 UserMapper.java 接口

java 复制代码
package com.example.demo.mapper;

import com.example.demo.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {
    //根据用户名查询添加
    @Select("select * from user where username = #{username}")
    User findByUserName(String username) ;
    //添加用户
    @Insert("insert into user(username,password,create_time,update_time) values(#{username},#{password},now(),now())")
    void add(String username, String password);
}
/*
@Mapper这是 MyBatis 提供的一个注解,用于标记 Mapper 接口。
当 Spring 扫描到这个注解时,它会为这个接口创建一个代理对象,
这个代理对象会实现接口中定义的所有方法,并处理 SQL 映射。

@Select这是 MyBatis 的注解,用于标记执行查询操作的方法。
在这个例子中,@Select 用于定义一个 SQL 查询语句,该语句根据提供的用户名查询用户。

@Insert这是 MyBatis 的注解,用于标记执行插入操作的方法。
在这个例子中,@Insert 用于定义一个 SQL 插入语句,该语句将新用户的信息插入到数据库中。
*/

/*
@Select("select * from user where username = #{username}")
User findByUserName(String username) ;
当 Spring 容器中的 MyBatis 代理对象调用 findByUserName 方法时,
MyBatis 框架会执行以下步骤:
将方法的 username 参数替换 SQL 语句中的 #{username} 占位符。
执行生成的 SQL 查询。
将查询结果(如果有的话)映射到 User 类的实例上。
返回 User 对象给调用者。
*/

创建 MD5 工具类

数据库密码一般都不使用明文,要进行加密

src/main/java/com/example/demo/utils 包下创建 Md5Util.java 工具类,用于密码加密。

java 复制代码
package com.example.demo.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Md5Util {
    /**
     * 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
     */
    protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    protected static MessageDigest messagedigest = null;

    static {
        try {
            messagedigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException nsaex) {
            System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
            nsaex.printStackTrace();
        }
    }

    /**
     * 生成字符串的md5校验值
     *
     * @param s
     * @return
     */
    public static String getMD5String(String s) {
        return getMD5String(s.getBytes());
    }

    /**
     * 判断字符串的md5校验码是否与一个已知的md5码相匹配
     *
     * @param password  要校验的字符串
     * @param md5PwdStr 已知的md5校验码
     * @return
     */
    public static boolean checkPassword(String password, String md5PwdStr) {
        String s = getMD5String(password);
        return s.equals(md5PwdStr);
    }


    public static String getMD5String(byte[] bytes) {
        messagedigest.update(bytes);
        return bufferToHex(messagedigest.digest());
    }

    private static String bufferToHex(byte bytes[]) {
        return bufferToHex(bytes, 0, bytes.length);
    }

    private static String bufferToHex(byte bytes[], int m, int n) {
        StringBuffer stringbuffer = new StringBuffer(2 * n);
        int k = m + n;
        for (int l = m; l < k; l++) {
            appendHexPair(bytes[l], stringbuffer);
        }
        return stringbuffer.toString();
    }

    private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
        char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
        // 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
        char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
        stringbuffer.append(c0);
        stringbuffer.append(c1);
    }

}

创建统一响应结果类

查看接口文档:得到响应的数据类型

src/main/java/com/example/demo/pojo 包下创建 Result.java 类,用于封装 HTTP 响应

java 复制代码
package com.example.demo.pojo;


import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

//统一响应结果
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
    private Integer code;//业务状态码  0-成功  1-失败
    private String message;//提示信息
    private T data;//响应数据

    //快速返回操作成功响应结果(带响应数据)
    public static <E> Result<E> success(E data) {
        return new Result<>(0, "操作成功", data);
    }

    //快速返回操作成功响应结果
    public static Result success() {
        return new Result(0, "操作成功", null);
    }

    public static Result error(String message) {
        return new Result(1, message, null);
    }
}
/*
 @NoArgsConstructor是Lombok 库提供的注解,用于自动为类生成无参构造函数。
如果类中没有其他构造函数,使用这个注解可以避免手动编写无参构造函数。

@AllArgsConstructor是 Lombok 库提供的注解,用于自动为类生成包含所有字段作为参数的构造函数。
这在需要通过构造函数初始化所有属性时非常有用。

@Data是 Lombok 库提供的注解,用于自动为类生成 getters、setters、
equals()、hashCode() 和 toString() 方法。这可以显著减少样板代码。

*/
  • 当调用 success(E data) 方法时,会创建一个新的 Result 对象,其 code 设置为 0message 设置为 "操作成功"data 设置为传入的 data 参数。
  • 当调用 success() 方法时,会创建一个新的 Result 对象,其 codemessagesuccess(E data) 方法相同,但 data 设置为 null
  • 当调用 error(String message) 方法时,会创建一个新的 Result 对象,其 code 设置为 1message 设置为传入的错误信息,data 设置为 null

Result<T> 是一个使用了泛型的Java类。让我们逐步解析一下:

Result: 这是一个类名,它被设计为一个容器或载体,用于封装从后端服务到前端或其他服务的数据传输结果。它通常包含了业务操作的状态、错误信息以及实际的数据。

<T>: 这是泛型的标记。T 是 Type 的缩写,它是一个占位符,代表任何类型的对象。当我们在定义 Result 类时,我们并不知道将来会存储哪种类型的数据,因此使用泛型 T 来表示可以存储任意类型的数据。

Result<T>: 当你看到 Result<T>,这意味着你可以创建一个 Result 类的对象,其中的 data 属性可以是任何类型的数据。例如,你可以有 Result<String>、Result<Integer> 或者 Result<List<User>> 等等。

实例化与使用:

如果你创建一个 Result<String> 对象,那么 T 就会被替换为 String 类型,意味着 data 属性将只能存储字符串。

如果你创建一个 Result<List<User>> 对象,T 将被替换为 List<User> 类型,意味着 data 属性将只能存储用户列表。

泛型的好处:

类型安全:编译器会在编译时检查类型,确保你不会把错误类型的对象放入 Result 中。

重用性:Result<T> 可以被用于多种类型的数据,提高了代码的灵活性和重用性。

所以,当你在代码中看到 Result<T>,它表示的是一个可以携带任意类型数据的结果对象。

在Java中,<E> Result<E> 是一个泛型方法的声明,它与泛型类的使用方式类似,但作用于方法级别。让我们详细解释一下这个声明:

<E>: 这个尖括号内的 E 定义了一个类型参数,它可以在方法内部作为类型占位符使用。E 通常代表"Element"或"Entity",但它可以是任何合法的标识符。

Result<E>: 这是方法的返回类型,其中 <E> 表示返回的 Result 对象将具有一个名为 data 的字段,该字段的类型将由调用此方法时传入的实际类型决定。这里的 E 必须与前面的 <E> 相匹配,确保方法返回的 Result 对象的 data 字段类型与方法参数 data 的类型相同。

public static <E> Result<E> success(E data): 整体来看,这是一个静态泛型方法,它接收一个类型为 E 的参数 data,并返回一个类型为 Result<E> 的对象。这意味着无论你传递什么类型的参数给这个方法,返回的 Result 对象的 data 字段都将具有相同的类型。

例如,如果你这样调用该方法:

Result<String> result = Result.success("Hello, World!");

这里,E 被实例化为 String 类型,所以 result 将是一个 Result<String> 类型的对象,它的 data 字段将存储一个字符串 "Hello, World!"。

再比如

Result<Integer> result = Result.success(42);

这次,E 被实例化为 Integer 类型,所以 result 将是一个 Result<Integer> 类型的对象,它的 data 字段将存储整数 42。

通过使用泛型方法,你可以编写更加灵活且类型安全的代码,而无需为每种数据类型都编写一个专门的方法。

写在最后:搞了好久还遇到问题,难鼠了

出现HTTP 406 错误

原因:服务器无法满足请求者(客户端)所接受的响应内容类型。在这个情况下,客户端发送了一个 POST 请求到 /user/register 接口,期望接收 application/json 类型的数据,但是服务器没有正确处理这个请求,或者没有返回正确的 Content-Type

解决方法:在返回结果的实体类里添加@Data

最后的最后,成功了

使用 Lombok 的 @Data 注解可以自动生成必要的 getter 和 setter 方法,从而解决因缺少访问器方法而导致的 JSON 序列化问题。

为什么使用 @Data 注解可以解决问题?

  1. 对象序列化 :在 Spring Boot 应用中,返回的对象通常需要被序列化为 JSON 格式。这通常是通过 Spring Boot 默认的 HttpMessageConverter 实现的,比如 MappingJackson2HttpMessageConverter

  2. Lombok 注解 :Lombok 是一个 Java 库,它通过注解处理器在编译时自动生成常见的 Java 代码,如 getter、setter、构造函数、toString 方法等。@Data 注解是 Lombok 提供的一个强大注解,它等价于同时使用 @Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor

  3. 自动生成访问器 :当你在一个类上使用 @Data 注解时,Lombok 会自动为该类的所有字段生成 getter 和 setter 方法。这对于 JSON 序列化非常重要,因为 JSON 库(如 Jackson)需要通过这些访问器方法来访问对象的属性。

  4. 解决序列化问题:如果没有 getter 方法,JSON 序列化库可能无法访问对象的属性,从而导致序列化失败。这可能间接导致 Spring 返回 406 错误,因为客户端期望的是 JSON 响应,但服务器没有正确生成。

  5. 简化代码:使用 Lombok 可以大大减少样板代码,使开发者能够专注于业务逻辑。

参数校验

上面的代码并没有进行参数校验

一般使用Spring Validation

Spring Validation是Spring提供的一个参数校验框架,使用预定义的注解完成参数校验

1.引入Spring Validation起步依赖

记得刷新!!!

2.在需要校验的参数前面添加@Pattern注解

5~16位非空字符用正则表达式为 \S{5,16},由于"\"是转义字符,在前面再加一个\,如图:

3.在类上加上@Validated注解

不符合参数要求的请求失败了,但是返回结果格式不符合要求

解决方法:可以使用全局异常处理器处理参数校验的异常

在启动类下新建一个exception包,定义一个类GlobalExceptionHandler

这个处理器使用@RestControllerAdvice注解来声明它将作为全局的异常处理控制器,并且@ExceptionHandler(Exception.class)注解表明它将处理所有类型的Exception

handleException方法中,首先调用e.printStackTrace()来打印堆栈跟踪信息,这有助于开发过程中定位问题。然后,使用StringUtil.notNullNorEmpty方法检查异常消息是否为空或非空,如果非空则直接返回异常消息,否则返回默认的错误信息"操作失败"。最后,返回一个Result对象,用于封装响应体和状态码

结果:

相关推荐
皮皮林5517 小时前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
用户908324602733 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840824 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解4 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解4 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记4 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者5 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840825 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解5 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者6 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq