前后端结合(三)

前言

  • 从下午到晚上, 构建了springboot的后台然后将后台和前端关联起来, 前端发送请求后端接收, 完成简单的登录逻辑判断. 由于是新手花费了很多时间. 感觉后端分层并不是很清晰.同时很多技术问题,仅仅是复制黏贴, 先追求跑通代码, 没有理解运行细节, 如果错误希望大佬不吝赐教.

前端请求

疑问1: axios请求

  • 前端发表单数据post请求,应该采用什么格式? form-data/form-urlencoded/json
// 表单信息
loginForm: {
username: 'admin',
password: '123456'
} 
// 发出axios请求 
await this.$http.post('login', Qs.stringify(this.loginForm))

后端搭建

  • 前端只要能发出请求包含用户信息就行了, 先不管这么多了, 开始springboot吧. 今天的目标是完成一个输入用户名密码验证跳转.

后端技术

  1. mysql 8.0.20

  2. mybatis plus

  3. druid

  4. springboot 2.x

创建Springboot项目

  1. 引入依赖
  • druid 连接池直接用的starter
  • mybatis plus
  • jackson 用于处理json数据
  • lombok 用于偷懒
  • spring-boot-devtools 热部署工具
  • mysql-connector-java 连接mysql

基本就是这些了

<?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 https://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.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.alex</groupId>
    <artifactId>alex-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>alex-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>14</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-mockmvc</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>
    </dependencies>
</project>

连接数据库

1. 查看数据库表单

这是直接前端项目拿的配套数据库sql脚本, 有点繁琐其实…

其中mg_name, mg_pwd为用户名,密码, 其他是用户信息.

CREATE TABLE `sp_manager` (
  `mg_id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `mg_name` varchar(32) NOT NULL COMMENT '名称',
  `mg_pwd` char(64) NOT NULL COMMENT '密码',
  `mg_time` int unsigned NOT NULL COMMENT '注册时间',
  `role_id` tinyint NOT NULL DEFAULT '0' COMMENT '角色id',
  `mg_mobile` varchar(32) DEFAULT NULL,
  `mg_email` varchar(64) DEFAULT NULL,
  `mg_state` tinyint DEFAULT '1' COMMENT '1:表示启用 0:表示禁用',
  PRIMARY KEY (`mg_id`)
) ENGINE=InnoDB AUTO_INCREMENT=511 DEFAULT CHARSET=utf8 COMMENT='管理员表';

2. 建立pojo类

根据mybatis plus 官网教程走, 建立一个和数据库表名一致的实体类

pojo/MgRole

@Data
@TableName("sp_manager")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MgRole {
    @TableId
    @JsonProperty("id")
    private int mg_id;
    private String mg_name;
    @JsonIgnore
    private String mg_pwd;
    private int mg_time;
    private int role_id;
    private String mg_mobile;
    private String mg_email;
    private int mg_state;
}

3. 建立Mapper接口

  1. 这里@Repository标记 才不会有注入时的错误提示. 但是不加也无所谓(小坑)
  2. 其次mybatis plus 不用重写任何方法

mappers/RoleMapper

@Repository
public interface RoleMapper extends BaseMapper<MgRole> {

}

4. 配置数据库连接信息

  • 这是基本格式了, 如果复制记得要改掉(数据库表名, 用户名, 密码) 三个信息.
  • 犯了点小错, 实践的时候直接加上了druid连接池, 导致报错处理了蛮久, 应该先确认基本数据库能够使用, 再使用连接池.

resources/application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/vuedb?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: xxxxxxx
    

5. 测试失败

  • 细节直接在RoleMapper里, 用快捷键command + N 生成测试类, 保持包名一致.
    • 但是结果一直返回null, 查询不到数据, 应该是表名, 字段出了些问题.

6. 更简单的测试类

  • 这里我自己建了了一个2行的数据表来测试, 通过即可下一步
  • 注意@SpringBootTest和junit test的引用, 和我一致(小坑)
  • 其次表名上面可以加上@TableName(“test”) 保持表名和数据库表名一致

测试Mapper

@Repository
public interface TestMapper extends BaseMapper<ForTest> {

}

测试实体类

@TableName("test")
@Data
public class ForTest {
    private int id;
    private String name;
}



测试用例

import cn.alex.pojo.ForTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class MapperTest {

    @Autowired
    private TestMapper mapper;

    @Test
    public void test() {
        List<ForTest> list= mapper.selectList(null);
        ForTest t = mapper.selectById(1);
        System.out.println(t);
        list.forEach(System.out::println);

    }
}

7.排查

经过一番搜索, 问题应该在于字段名映射出问题了, 可以打开mybatis plus的log日志, 同时数据库这张表实在是太难受了, 字段名mg_name下划线, 但是前端需要的字段是username, 有点烦恼, 不过没事 先把后端做好.

mybatis-plus:
  configuration:
    # mybatis plus日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 类 mg_name 字段 mg_name 应该关闭
    map-underscore-to-camel-case: false

加上了log日志 能直接看到mybatis生成的查询语句, map-underscore-to-camel-case意思应该是不把下划线映射到驼峰. 因为我这里都是用下划线形式.改为false.

加入连接池

  • 凭印象感觉, mybatis-plus应该是一个简化jdbc操作的东西, 而连接池可以为我们省去手动创建数据库连接.看了网上很多资料,都还在写配置类, 现在都有druid starter了 直接用了, 不需要写任何配置类了

只需要在application.yml这样写即可

server:
  servlet:
    # 后台地址
    context-path: /api
    # 端口号
  port: 8080

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/vuedb?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: xxxxx
    druid:
      #初始化大小
      initialSize: 5
      #最小值
      minIdle: 5
      #最大值
      maxActive: 20
      #最大等待时间,配置获取连接等待超时,时间单位都是毫秒ms
      maxWait: 60000
      #配置间隔多久才进行一次检测,检测需要关闭的空闲连接
      timeBetweenEvictionRunsMillis: 60000
      #配置一个连接在池中最小生存的时间
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,
      #'wall'用于防火墙,SpringBoot中没有log4j,我改成了log4j2
      filters: stat,wall,log4j2
      #最大PSCache连接
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
      # 配置StatFilter
      web-stat-filter:
        #默认为false,设置为true启动
        enabled: true
        url-pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      #配置StatViewServlet
      stat-view-servlet:
        url-pattern: "/druid/*"
        #允许那些ip
        allow: 127.0.0.1
        login-username: admin
        login-password: 123456
        #禁止那些ip
        deny: 192.168.1.102
        #是否可以重置
        reset-enable: true
        #启用
        enabled: true
  • 因为我配置了后端服务器挂载到localhost:8080/api下

浏览器输入localhost:8080/api/druid/stat 便可访问druid管理页面, 这样连接池就构建好了, 不需要再有任何操作了.

Controller

  • 配置如何接收请求

这里的细节在于, 前端发出了一个post请求我该如何接收, 暂时用的是@RequestParam(“username”)这种方式, 如果有这个字段的映射就能拿到数据. 目前是成功的. 但应该不是最好的方式, 对象可能比较合适, 是否要建一个新的对象呢? 等我下次试试

@RestController
public class LoginController {
    @Autowired
    private LoginService loginService;

    @PostMapping("/login")
    public Result login(@RequestParam("username") String username, @RequestParam("password") String password) {
        return loginService.validate(username, password);
    }
}

Service层

  • 目前的理解是用于查询数据库, 同时判断用户名密码是否符合, 最后返回封装好的对象. 原本是dao层来处理sql的, 但现在用了mybatis plus, mapper是个接口了. 所以数据库语句直接写在service层了(没有考虑很多)

  • 目前只做了基本判断

@Service
public class LoginService {
    @Autowired
    private RoleMapper roleMapper;

    public Result validate(String username, String password) {
        Result res = null;
//        username = "admin";
//        password = "123456";
        MgRole mgRole = roleMapper.selectOne(new QueryWrapper<MgRole>()
                .select("mg_id", "role_id", "mg_name","mg_mobile","mg_email")
                .eq("mg_name", username)
                .eq("mg_pwd", password));
        System.out.println(username + " " + password);
        if (mgRole == null) {
            System.out.println(mgRole);
            res = ResultFactory.buildFailResult("登录失败");
        }
        res = ResultFactory.buildSuccessResult(mgRole);
        return res;
    }
}

查询语句

  • 我只是照葫芦画瓢写了一下, 感觉应该还有更好的方式.
  MgRole mgRole = roleMapper.selectOne(new QueryWrapper<MgRole>()
                .select("mg_id", "role_id", "mg_name","mg_mobile","mg_email")
                .eq("mg_name", username)
                .eq("mg_pwd", password));

JSON对象

  • Controller层是能够直接返回对象作为响应体的, Springboot会进行自动类型转换, 转成json给前端. 但是!!!

前端要求的数据是, 并不仅仅是实体类的某些属性, 还要加上meta对象 包括字符串信息和状态码

{
    "data": {
        "id": 500,
        "rid": 0,
        "username": "admin",
        "mobile": "123",
        "email": "123@qq.com",
     }
    "meta": {
        "msg": "登录成功",
        "status": 200
    }
}

因此又写了一个返回对象Result类

  • @JsonProperty(“data”) 用来指定返回json时的属性名称(很好用!)
package cn.alex.result;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Result {
    @JsonProperty("data")
    private Object result;
    @JsonProperty("meta")
    private Meta meta;
    Result(int code, String message, Object data) {
        this.meta = new Meta(code, message);
        this.result = data;
    }
    @Data
    private class Meta {
        private int code;
        private String message;

        public Meta(int code, String message) {
            this.message = message;
            this.code = code;
        }
    }
}

状态码 枚举类

public enum ResultCode {
    SUCCESS(200),
    FAIL(400),
    UNAUTHORIZED(401),
    NOT_FOUND(404),
    INTERNAL_SERVER_ERROR(500);

    public int code;

    ResultCode(int code) {
        this.code = code;
    }
}

Result对象工厂类

public class ResultFactory {

    public static Result buildSuccessResult(Object data) {
        return buildResult(ResultCode.SUCCESS, "成功", data);
    }

    public static Result buildFailResult(String message) {
        return buildResult(ResultCode.FAIL, message, null);
    }

    public static Result buildResult(ResultCode resultCode, String message, Object data) {
        return buildResult(resultCode.code, message, data);
    }

    public static Result buildResult(int resultCode, String message, Object data) {
        return new Result(resultCode, message, data);
    }
}

测试: 跨域问题

一顿操作以后, 后端算是写好了, 在前端发出请求的时候,

前端响应 状态码:403

​ 请求方法: options

​ mode: cors 跨域请求 因为我的前后端端口号不一致, 并且post请求不是一个"简单请求"

  • 经过查阅只需要加一个配置类即可

config/MyWebMvcConfiguer

@Configuration
public class MyWebConfigurer implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //所有请求都允许跨域,使用这种配置方法就不能在 interceptor 中再配置 header 了
        registry.addMapping("/**")
                .allowCredentials(true)
                .allowedOrigins("http://localhost:8081")//改成前端地址
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}

解决啦! 最后前端就可以通过表单提交,得到验证信息通过登录测试了.

遗留问题(xiaciyiding)

  • JWT token 没弄
  • 前后端数据对接问题, 前端应该发json后端接收json比较好吧, 有必要把前端发送的数据封装成一个新的对象吗
  • 跨域问题? 有什么意义吗?
  • 是否该加一个dao层
  • 如果仅仅想返回对象的部分数据 还要封装成json 该怎么做(目前做法是用@JsonInclude(JsonInclude.Include.NON_NULL 直接排除了null的元素, 应该有一些场景数据就是要返回null的吧这样应该不太合适)
  • mybatis-plus更高级的用法…
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Leaflet是一个开源的地图库,用于在web上创建交互式地图。它可以与Flask框架结合使用,以创建基于地图的应用程序。以下是在Flask中使用Leaflet的一些步骤: 1. 安装Flask和Leaflet 要在Flask中使用Leaflet,需要先安装这两个库。可以使用pip进行安装: ``` pip install flask pip install flask-leaflet ``` 2. 创建Flask应用程序 接下来,需要创建一个Flask应用程序。可以使用以下代码来创建一个简单的应用程序: ``` from flask import Flask, render_template from flask_leaflet import Leaflet app = Flask(__name__) leaflet = Leaflet(app) @app.route('/') def index(): return render_template('index.html') if __name__ == '__main__': app.run(debug=True) ``` 代码中,使用Flask创建一个应用程序,并使用Flask-Leaflet扩展来初始化Leaflet。然后,创建一个路由函数来处理根路径。在这个函数中,使用`render_template`函数来渲染一个HTML模板。 3. 创建HTML模板 接下来,需要创建一个HTML模板来显示地图。可以使用以下代码来创建一个简单的模板: ``` <!DOCTYPE html> <html> <head> <title>Flask Leaflet Example</title> <link rel="stylesheet" href="{{ url_for('leaflet.css') }}" /> <script src="{{ url_for('leaflet.js') }}"></script> </head> <body> <div id="map"></div> <script> var map = L.map('map').setView([51.505, -0.09], 13); L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { attribution: '© <a href="https://www.mapbox.com/">Mapbox</a>', maxZoom: 18, id: 'mapbox/streets-v11', tileSize: 512, zoomOffset: -1, accessToken: 'your.mapbox.access.token' }).addTo(map); </script> </body> </html> ``` 代码中,使用`url_for`函数来加载Flask-Leaflet扩展中的CSS和JavaScript文件。然后,在`<div>`标签中创建一个地图容器,并使用JavaScript代码来初始化地图。在这个例子中,使用Mapbox提供的地图瓦片服务。 4. 运行应用程序 最后,可以使用以下命令来运行应用程序: ``` python app.py ``` 在浏览器中输入`http://localhost:5000/`可以看到一个基本的Leaflet地图。可以使用Leaflet的API来添加自定义的标记、线条和多边形等元素,创建更复杂的地图应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值