Front-end and back-end separation project-Introduction to back-end interface development notes

1. Create a project

1. Create a new maven project

If no project is open, click New Project directly . If other projects are already open, click in sequence File -> New -> Project -> Maven, click Next , and create a new Maven project (we are using version 1.8 of the Project SDK here)

Insert image description here
Then we configure the Maven project as follows (you can find the specific meaning by yourself)

  • Name , project name
  • Location , the local storage path of the project
  • GroupId , Maven group is usually written upside down by the website, you can also name it yourself, such as cn.zhangsan
  • ArtifactId , the name of the module in the group
  • Version , the version number of the Maven project

Insert image description here

After clicking Finnish (if other projects have been opened, the Open Project window will pop up. Selecting This Window will close the current project and open the created project. Selecting New Window will open the created project in a new window). The initial Maven project will be Created
Insert image description here

2. Build SpringBoot

Using SpringBoot is very simple. We only need to add SpringBoot dependency in pom.xml . The final result is as follows

  • parent , set SpringBoot as the parent of the current project, that is, our project inherits SpringBoot
  • properties , we usually write the dependency version number here and quote it directly below to facilitate version management.
  • dependencies , dependency list
  • dependency , dependency
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
    </parent>

    <groupId>com.jl15988</groupId>
    <artifactId>we-shopping</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

To load Maven dependencies, you need to click the small icon in the upper right corner to introduce the dependencies into our project.
Insert image description here

3. Create interface

src\main\javaCreate a new package below com.jl15988.shopping(the package name is often the same as the group name, or group name + module name, but it cannot be capitalized), then create a WeShoppingApplicationclass (named here using the project name + Application), and add the following code

package com.jl15988.shopping;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Jalon
 * @since 2023/8/1 10:56
 **/
@SpringBootApplication
public class WeShoppingApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(WeShoppingApplication.class);
    }
}

com.jl15988.shoppingThen create controllera package under it, create a class under the controller package HelloController, and add the following code

  • @RestController , annotation, used to indicate that the current controller class is
  • @GetMapping , declares that this method is a Getrequest
package com.jl15988.shopping.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Jalon
 * @since 2023/8/1 10:59
 **/
@RestController
public class HelloController {
    
    

    @GetMapping("/hello")
    public String hello() {
    
    
        return "Hello World!";
    }
}

Then we can start the project. We can directly click on the WeShoppingApplicationgreen triangle to start the project (if you have started the project, you can directly click on the small green triangle in the upper right corner)
Insert image description here

Then the browser accesses localhost:8080/hello, and the rendering is as follows (if other projects or programs occupy port 8080, an error may be reported)

Insert image description here

2. Development interface

1. Create database

Create a database using navicatwe_shopping

Insert image description here

Then execute the sql statement to create the data table

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `username` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `nick` varchar(26) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `create_time` datetime(0) NULL DEFAULT NULL,
  `update_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

2. Add code generation tools

First introduce dependencies into the project

<!-- 数据库 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<!-- mybatis-plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
<!-- 代码生成 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3.1</version>
</dependency>
<!-- 代码生成引擎模板 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
    <version>3.1.0</version>
</dependency>

com.jl15988.shoppingThen create CodeGeneratora class under and add the following content

package com.jl15988.shopping;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;

import java.sql.Types;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author Jalon
 * @since 2023/8/1 11:35
 **/
public class CodeGenerator {
    
    

    // 数据库地址
    private static final String URL = "jdbc:mysql://localhost:3306/we_shopping?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";

    // 数据库用户名
    private static final String USERNAME = "root";

    // 数据库密码
    private static final String PASSWORD = "123456";

    // 生成代码的包名
    private static final String PACKAGE = "com.jl15988.shopping";

    // 作者
    private static final String AUTHOR = "Jalon";

    // 过滤的数据库名前缀
    private static final String[] PREFIXS = {
    
    "sys_"};

    public static void main(String[] args) {
    
    
        String projectPath = System.getProperty("user.dir");

        FastAutoGenerator.create(URL, USERNAME, PASSWORD)
                .globalConfig(builder -> {
    
    
                    builder.author(AUTHOR) // 设置作者
//                            .enableSwagger() // 开启 swagger 模式
                            .outputDir(projectPath + "/src/main/java") // 输出目录
                            .disableOpenDir(); //
                })
                .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
    
    
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.SMALLINT) {
    
    
                        // 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    return typeRegistry.getColumnType(metaInfo);

                }))
                .packageConfig(builder -> {
    
    
                    builder.parent(PACKAGE) // 设置父包名
//                            .moduleName("") // 设置父包模块名
                            .entity("domain.entity")
                            .pathInfo(Collections.singletonMap(OutputFile.xml, projectPath + "/src/main/resources/mapper")); // 设置mapperXml生成路径
                })
                .strategyConfig((scanner, builder) -> {
    
    
                    List<String> tables = getTables(scanner.apply("请输入表名,多个英文逗号分隔,所有表生成输入 all"));
                    builder.addTablePrefix(PREFIXS) // 过滤前缀
                            .addInclude(tables) // 增加表匹配
                            // controller配置
                            .controllerBuilder()
                            .enableRestStyle() // 添加@RestController
                            .enableHyphenStyle() // 驼峰转连字符
                            // 实体类配置
                            .entityBuilder()
                            .enableFileOverride() // 生成覆盖
                            .enableLombok() //添加lombok
                            .addTableFills(new Column("create_time", FieldFill.INSERT))
                            .disableSerialVersionUID() // 禁用生成 serialVersionUID
                            .idType(IdType.ASSIGN_ID) // 当用户未输入时,采用雪花算法生成一个适用于分布式环境的全局唯一主键
                            .build();
                })
                // 引擎模板,默认的是Velocity引擎模板
//                .templateEngine(new BeetlTemplateEngine()) // Beetl引擎模板
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板
                .execute();
    }

    // 处理 all 情况
    protected static List<String> getTables(String tables) {
    
    
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
}

Then click on the small green triangle to start the main method in this class, and then enter in the console allto automatically generate the various classes we need, as shown below (ignore application.yml here)
Insert image description here

Take a note, optimized here

2. Write interface

Then create a file under project resources application.ymlwith the following content (note to modify your database name and password)

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/we_shopping?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
mybatis:
  mapper-locations: classpath:/mapper/*Mapper.xml

Then com.jl15988.shoppingcreate the entity package under the package, and then create the User class as follows

package com.myblog.entity;

import lombok.Data;

import java.util.Date;

/**
 * @author Jaon
 * @datetime 2021/10/29 16:13
 */
@Data
public class User {
    
    

    private Long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 昵称
     */
    private String nick;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新时间
     */
    private Date updateTime;
}

Create the mapper package under the com.myblog package, and then create the UserMapper interface, as follows

package com.myblog.mapper;

import com.myblog.entity.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * @author Jaon
 * @datetime 2021/10/29 16:09
 */
@Mapper
public interface UserMapper {
    
    

    List<User> list();
}

Create the mapper package under resources, and then create UserMapper.xml as follows (the sql statement written here is to query all entries in the user table of the database. If you query all field values, it is not recommended to use the * sign. It is recommended to query all field values ​​by hand)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.myblog.mapper.UserMapper">
    <select id="list" resultType="com.myblog.entity.User">
        SELECT id, username, nick, email, create_time createTime, update_time updateTime
        FROM t_user
    </select>
</mapper>

Create the service package under the com.myblog package, and then create the UserService interface as follows

package com.myblog.service;

import com.myblog.entity.User;

import java.util.List;

/**
 * @author Jaon
 * @datetime 2021/10/29 16:11
 */
public interface UserService {
    
    

    List<User> list();
}

Create the impl package under service, create the UserServiceImpl class and implement the UserService interface as follows

package com.myblog.service.impl;

import com.myblog.entity.User;
import com.myblog.mapper.UserMapper;
import com.myblog.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author Jaon
 * @datetime 2021/10/29 16:11
 */
@Service
public class UserServiceImpl implements UserService {
    
    

    @Autowired
    UserMapper userMapper;

    @Override
    public List<User> list() {
    
    
        return userMapper.list();
    }
}

Create UserController under controller as follows

package com.myblog.controller;

import com.myblog.entity.User;
import com.myblog.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author Jaon
 * @datetime 2021/10/29 16:22
 */
@RestController
@RequestMapping("/user")
public class UserController {
    
    

    @Autowired
    UserService userService;

    @GetMapping("/list")
    public List<User> list() {
    
    
        return userService.list();
    }
}

Finally, create a piece of data in the database, restart the project, and access localhost:8080/user/list

as in database

Insert image description here

Access results (I use the CSDN plug-in here, so the return data is automatically formatted)

Insert image description here

a. Time formatting

As shown in the figure, the time format returned by the backend does not match the format we actually want, so we need to convert the following format. You can directly add the @JsonFormat annotation on the entity class attribute.

/**
 * 创建时间
 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;

/**
 * 更新时间
 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;

b. Hide attributes with empty data

As shown in the figure, the returned data we see contains null parameters. In some cases, we may hide the null parameters. We can use the @JsonInclude annotation to achieve this.

@Data
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class User {
    
    
    /* 省略代码 */
}

3. Implement cross-domain

If it is a project with separate front-end and back-end, if we create a front-end project to access the current interface, it will not be accessible because cross-domain requests are caused by different addresses or ports. At this time, we need to open cross-domain requests on the back end, as shown in the figure

Insert image description here

Create the config package under the com.myblog package and create the WebMvcConfig class as follows

package com.myblog.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Jaon
 * @datetime 2021/11/1 9:54
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    

    @Override
    public void addCorsMappings(CorsRegistry registry) {
    
    
        // 允许所有的路径可以跨域
        registry.addMapping("/**")
                // 允许所有来源都可以跨域
                .allowedOriginPatterns("*")
                // 允许跨域的请求
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                // 允许证书凭证(如果这里设置为true,设置来源为所有只能使用allowedOriginPatterns)
                .allowCredentials(true)
                // 跨域时间3600秒
                .maxAge(3600)
                // 允许所以头标题
                .allowedHeaders("*");
    }
    }
}

Then we restart the project and revisit it and find that the cross-domain error has disappeared and the data can be returned, as shown in the figure

Insert image description here

4. Global exception handling

As a backend, even if an error is reported, exception information needs to be returned to prompt the frontend. However, in our code, the data transferred between the controller layer and the service layer cannot be limited to a very uniform data type or the service has no return value at all. This results in some exception information that cannot be returned directly from the service layer to the front end. It would be too troublesome to use try-catch to capture it, so we define global exception handling to uniformly capture exceptions and return them to the front end.

As shown in the picture, we wrote a very simple code that can definitely report an error

Insert image description here

Then when we access the address, we will find that a 500 error is reported and the error message is printed on the console.

Insert image description here

Insert image description here

Then we create the handler package under the com.myblog package and create the GlobalExceptionHandler class, as follows

package com.myblog.handler;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * @author Jaon
 * @datetime 2021/11/1 11:27
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
    
    
        return e.getMessage();
    }
}

Then restart the project and access the address again, and find that the return information has changed. This means that we have caught the exception and returned it to the front end, but there is no error message in the console.

Insert image description here

a. Error log

For a back-end project, the error log is very important and plays a decisive role in later maintenance and problem solving. Therefore, it is a bad phenomenon that global exception processing does not print error information. We can use @Slf4j to implement error message logging, as follows

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
    
    
        log.error("报错信息:{}", e.getMessage());
        return e.getMessage();
    }
}

b. Custom exception

In our business logic, we may need to customize exceptions to be thrown and handled by the global exception handling class. At this time, considering the difference in returned status codes and exception information, we need to customize exception classes to cooperate with global exception handling.

Create the common.lang package under the com.myblog package and create the MyBlogException class as follows

package com.myblog.common.exception;

import lombok.Getter;

/**
 * @author Jaon
 * @datetime 2021/11/1 12:32
 */
@Getter
public class MyBlogException extends RuntimeException {
    
    

    private Integer code;

    private String msg;

    public MyBlogException(Integer code, String msg) {
    
    
        super(msg);
        this.code = code;
        this.msg = msg;
    }

    public MyBlogException(Integer code, String msg, Throwable throwable) {
    
    
        super(msg, throwable);
        this.code = code;
        this.msg = msg;
    }

    public MyBlogException(String msg) {
    
    
        super(msg);
        this.code = 403;
        this.msg = msg;
    }

    public MyBlogException(String msg, Throwable throwable) {
    
    
        super(msg, throwable);
        this.code = 403;
        this.msg = msg;
    }
}

Then we can use a custom exception class to throw exceptions, such as

Modify the list interface

@GetMapping("/list")
public List<User> list() {
    
    
    List<User> list = userService.list();
    if (list.size() <= 0) {
    
    
        // 如果数据库中没有用户信息则抛出异常
        throw new MyBlogException("当前没有用户信息");
    }
    return list;
}

Add method in global exception handling class

@ExceptionHandler(MyBlogException.class)
public Map<String, Object> handleException(MyBlogException e) {
    
    
    log.error("报错信息:{}", e.getMessage());
    Map<String, Object> map = new HashMap<>();
    map.put("code", e.getCode());
    map.put("msg", e.getMsg());
    return map;
}

Then delete all the information in the user table, and then request again. You can see that the front end received the results we wanted.

Insert image description here

5. Unified encapsulation of return data

For projects with separate front-end and back-end, the data returned by the back-end often needs to be unified and standardized to coordinate with the unified processing of front-end data.

Create the lang package under the common package and create the Result class as follows

package com.myblog.common.lang;

import lombok.Data;

/**
 * @author Jaon
 * @datetime 2021/11/1 13:14
 */
@Data
public class Result {
    
    

    private Integer code;

    private String msg;

    private Object data;

    public static Result success() {
    
    
        return success(null);
    }

    public static Result success(Object data) {
    
    
        return success(200, data);
    }

    public static Result success(Integer code, Object data) {
    
    
        return common(code, "操作成功", data);
    }

    public static Result fail() {
    
    
        return fail(null);
    }

    public static Result fail(Integer code, String msg) {
    
    
        return common(code, msg, null);
    }

    public static Result fail(String msg) {
    
    
        return common(400, msg, null);
    }

    public static Result fail(Integer code, Object data) {
    
    
        return common(code, "操作失败", data);
    }

    public static Result common(Integer code, String msg, Object data) {
    
    
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
}

Then replace all returns with

// 修改接口返回值
@GetMapping("/list")
public Result list() {
    
    
    List<User> list = userService.list();
    if (list.size() <= 0) {
    
    
        throw new MyBlogException("当前没有用户信息");
    }
    return Result.success(list);
}
// 修改异常处理类返回值
@ExceptionHandler(MyBlogException.class)
public Result handleException(MyBlogException e) {
    
    
    log.error("报错信息:{}", e.getMessage());
    return Result.fail(e.getCode(), e.getMsg());
}

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
    
    
    log.error("报错信息:{}", e.getMessage());
    return Result.fail(e.getMessage());
}

Then we access the interface and we can see that the return format has changed.

Insert image description here

At this point, our entry interface development is complete.

Guess you like

Origin blog.csdn.net/jl15988/article/details/121079092