学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域

目录

1.【内容模块】需求分析

2.【内容模块】模块工程的结构

3.【课程查询功能1】通用

3.1 分析数据模型

3.2 mybatis-plus代码生成器

3.3 内容模块聚合api,model,service模块

3.4 接口设计分析

3.5 【基础模块】分页查询模型类

3.6【基础模块】日期配置类

3.7【内容模块】分页插件拦截器

3.8【内容模块】查询条件模型类 

3.9【内容模块】响应模型类

4.swagger

4.1 swagger生成接口文档步骤 

4.2 swagger常用注解

4.3 设置swagger信息,@Api和@ApiOperation

4.4 优化分页模型类,用@ApiModelProperty描述字段

4.5 使用Swagger进行接口测试

5.【课程查询功能2】具体

5.1【习惯】先写持久层,再写Service

5.2【内容-接口模块】yml配置

5.3 创建审核状态数据库字典

5.4【业务】条件查询课程列表

5.5【接口】查询课程列表

5.6 swagger测试

5.7【idea插件】Httpclient测试

5.8 封装用来Httpclient测试的文件夹

5.9 使用idea启动前端项目

5.10 创建系统模块

5.11 解决CORS跨域问题

5.11.1 方法一(使用):系统模块-通过cors过滤器,添加跨域响应头 

5.11.2 方法二:实现接口WebMvcConfigurer

5.11.3 方法三:JSONP

5.11.4 方法四:使用nginx反向代理为同一域

5.12前后端联调


1.【内容模块】需求分析

过程:确认用户需求、 确认关键问题、梳理业务流程、数据建模、编写需求规格说明书。

确认用户需求:开发人员将用户抽象的需求转换为项目具体的功能、性能方面的要求。

确认关键问题:发布课程要发布哪些信息,发布了不良信息怎么办,用户怎么查看课程

梳理业务流程:首先分析核心业务流程,包括内容模块的课程发布、全项目的选课学习流程。

数据建模:根据关键信息建表。

编写需求规格说明书:针对每一个问题编写需求用例,例如添加课程功能的参与者、前置条件(机构仅允许向自己机构添加课程)、基本流程(展示课程页面、添加课程、录入哪些信息、提交)、数据描述、后置条件(插入记录)。

2.【内容模块】模块工程的结构

三个工程聚合在内容模块:接口工程(controller,为前端提供接口)、业务工程Service和dao,数据模型工程(实体类、数据传输对象dto)

3.【课程查询功能1】通用

3.1 分析数据模型

分析数据模型,查哪个数据库、查哪些信息,查询条件。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第1张图片

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第2张图片

3.2 mybatis-plus代码生成器

使用mybatis-plus的generator工程,根据内容模块相关的数据库表生成PO类、Mapper接口、Mapper的xml文件。

地址在:GitHub - baomidou/generator: Any Code generator

修改数据库信息、生成路径,运行main方法:

        //数据库账号
        private static final String DATA_SOURCE_USER_NAME  = "root";
        //数据库密码
        private static final String DATA_SOURCE_PASSWORD  = "mysql";
        //生成的表
        private static final String[] TABLE_NAMES = new String[]{
                "course_base",
                "course_market",
                "course_teacher",
                "course_category",
                "teachplan",
                "teachplan_media",
                "course_publish",
                "course_publish_pre"
        };
        // TODO 默认生成entity,需要生成DTO修改此变量
        // 一般情况下要先生成 DTO类 然后修改此参数再生成 PO 类。
        private static final Boolean IS_DTO = false;

        public static void main(String[] args) {
                ....
                //生成路径
                gc.setOutputDir(System.getProperty("user.dir") + "/xuecheng-plus-generator/src/main/java");
                
        ....
        // 数据库配置
                DataSourceConfig dsc = new DataSourceConfig();
                dsc.setDbType(DbType.MYSQL);
                dsc.setUrl("jdbc:mysql://192.168.101.65:3306/xcplus_" + SERVICE_NAME+"166"
                                + "?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf8");
                ...

运行后会自动在内容模块下生成生成PO类、Mapper接口、Mapper的xml文件。

3.3 内容模块聚合api,model,service模块

api就是controller,model就是模型。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第3张图片

3.4 接口设计分析

分析请求方式、请求参数、请求类型(json)、响应类型、状态码

#请求类型
POST /content/course/list?pageNo=2&pageSize=1
Content-Type: application/json
#请求参数
{
  "auditStatus": "202002",
  "courseName": "",
  "publishStatus":""
}
#成功响应结果
{
  "items": [
    {
      "id": 26,
      "companyId": 1232141425,
      "companyName": null,
      "name": "spring cloud实战",
      "users": "所有人",
      "tags": null,
      "mt": "1-3",
      "mtName": null,
      "st": "1-3-2",
      "stName": null,
      "grade": "200003",
      "teachmode": "201001",
      "description": "本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。",
      "pic": "https://cdn.educba.com/academy/wp-content/uploads/2018/08/Spring-BOOT-Interview-questions.jpg",
      "createDate": "2019-09-04 09:56:19",
      "changeDate": "2021-12-26 22:10:38",
      "createPeople": null,
      "changePeople": null,
      "auditStatus": "202002",
      "auditMind": null,
      "auditNums": 0,
      "auditDate": null,
      "auditPeople": null,
      "status": 1,
      "coursePubId": null,
      "coursePubDate": null
    }
  ],
  "counts": 23,
  "page": 2,
  "pageSize": 1
}

3.5 【基础模块】分页查询模型类

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第4张图片

在基础模块 

package com.xuecheng.base.model;

import lombok.Data;
import lombok.ToString;
import lombok.extern.java.Log;

/**
 * @description 分页查询通用参数
 * @author Mr.M
 * @date 2022/9/6 14:02
 * @version 1.0
 */
@Data
@ToString
public class PageParams {

  //当前页码
  private Long pageNo = 1L;

  //每页记录数默认值
  private Long pageSize =10L;

  public PageParams(){

  }

  public PageParams(long pageNo,long pageSize){
      this.pageNo = pageNo;
      this.pageSize = pageSize;
  }



}

3.6【基础模块】日期配置类

在base工程com.xuecheng.base.config包下加配置LocalDateTimeConfig 类实现转json时字符串与LocalDateTime类型的转换。

@Configuration
public class LocalDateTimeConfig {

    /*
     * 序列化内容
     *   LocalDateTime -> String
     * 服务端返回给客户端内容
     * */
    @Bean
    public LocalDateTimeSerializer localDateTimeSerializer() {
        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    /*
     * 反序列化内容
     *   String -> LocalDateTime
     * 客户端传入服务端数据
     * */
    @Bean
    public LocalDateTimeDeserializer localDateTimeDeserializer() {
        return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
    //long转string避免精度损失
    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        //忽略value为null 时 key的输出
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(module);
        return objectMapper;
    }

    // 配置
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            builder.serializerByType(LocalDateTime.class, localDateTimeSerializer());
            builder.deserializerByType(LocalDateTime.class, localDateTimeDeserializer());
        };
    }

}

3.7【内容模块】分页插件拦截器

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
//        溢出总页数后是否进行处理(默认不处理)
        paginationInnerInterceptor.setOverflow(true);
//        单页分页条数限制(默认无限制)
        paginationInnerInterceptor.setMaxLimit(1000L);

        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}

3.8【内容模块】查询条件模型类 

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第5张图片

package com.xuecheng.content.model.dto;

import lombok.Data;
import lombok.ToString;

/**
 * @description 课程查询参数Dto
 * @author Mr.M
 * @date 2022/9/6 14:36
 * @version 1.0
 */
 @Data
 @ToString
public class QueryCourseParamsDto {

  //审核状态
 private String auditStatus;
 //课程名称
 private String courseName;
  //发布状态
 private String publishStatus;

}

3.9【内容模块】响应模型类

package com.xuecheng.base.model;

import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.util.List;

/**
 * @description 分页查询结果模型类
 * @author Mr.M
 * @date 2022/9/6 14:15
 * @version 1.0
 */
@Data
@ToString
public class PageResult implements Serializable {

    // 数据列表
    private List items;

    //总记录数
    private long counts;

    //当前页码
    private long page;

    //每页记录数
    private long pageSize;

    public PageResult(List items, long counts, long page, long pageSize) {
        this.items = items;
        this.counts = counts;
        this.page = page;
        this.pageSize = pageSize;
    }



}

4.swagger

swagger:通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。

4.1 swagger生成接口文档步骤 

1.导入依赖

content.api包下 



    com.spring4all
    swagger-spring-boot-starter

2.bootstrap.yml配置swagger的扫描包路径及其它信息

server:
  servlet:
    context-path: /content    #应用的上下文路径,即项目路径,是构成请求url地址的一部分。
  port: 63041
swagger:
  title: "学成在线内容管理系统"
  description: "内容系统管理系统对课程相关信息进行管理"
  base-package: com.xuecheng.content
  enabled: true
  version: 1.0.0

3.启动类中添加@EnableSwagger2Doc注解

访问http://localhost:63040/content/swagger-ui.html查看接口信息

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第6张图片

这个文档存在两个问题:

1、接口名称显示course-base-info-controller名称不直观

2、课程查询是post方式只显示post /course/list即可。

4.2 swagger常用注解

 @Api:修饰整个类,描述Controller的作用
 @ApiOperation:描述一个类的一个方法,或者说一个接口
 @ApiParam:单个参数描述
 @ApiModel:用对象来接收参数
 @ApiModelProperty:用对象接收参数时,描述对象的一个字段
 @ApiResponse:HTTP响应其中1个描述
 @ApiResponses:HTTP响应整体描述
 @ApiIgnore:使用该注解忽略这个API
 @ApiError :发生错误返回的信息
 @ApiImplicitParam:一个请求参数
 @ApiImplicitParams:多个请求参数

@ApiImplicitParam属性如下:

属性

取值

作用

paramType

查询参数类型

path

以地址的形式提交数据

query

直接跟参数完成自动映射赋值

body

以流的形式提交 仅支持POST

header

参数在request headers 里边提交

form

form表单的形式提交 仅支持POST

dataType

参数的数据类型 只作为标志说明,并没有实际验证

Long

String

name

接收参数名

value

接收参数的意义描述

required

参数是否必填

true

必填

false

非必填

defaultValue

默认值

4.3 设置swagger信息,@Api和@ApiOperation

content.ap包下 

 @Api(value = "课程信息编辑接口",tags = "课程信息编辑接口")
 @RestController
public class CourseBaseInfoController {

 @ApiOperation("课程查询接口")
 @PostMapping("/course/list")    //设置请求方式后,swagger页面就只显示post请求
  public PageResult list(PageParams pageParams, @RequestBody(required=false) QueryCourseParamsDto queryCourseParams){

     //....

  }

}

再次启动服务,工程启动起来,访问http://localhost:63040/content/swagger-ui.html查看接口信息 

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第7张图片

点开请求:

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第8张图片

4.4 优化分页模型类,用@ApiModelProperty描述字段

 @ApiModelProperty

@ApiModelProperty:用对象接收参数时,描述对象的一个字段
@Data
@ToString
public class PageParams {

    //当前页码
    @ApiModelProperty("页码")
    private Long pageNo = 1L;
    //每页显示记录数
    @ApiModelProperty("每页记录数")
    private Long pageSize = 30L;

    public PageParams() {
    }

    public PageParams(Long pageNo, Long pageSize) {
        this.pageNo = pageNo;
        this.pageSize = pageSize;
    }
}

再次访问swagger页面,可以看到描述信息已经出来了:

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第9张图片

4.5 使用Swagger进行接口测试

content.ap包下 CourseBaseInfoController 

@ApiOperation("课程查询接口")
@PostMapping("/course/list")
public PageResult list(PageParams pageParams, @RequestBody(required=false) QueryCourseParamsDto queryCourseParams){

    CourseBase courseBase = new CourseBase();
    courseBase.setName("测试名称");
    courseBase.setCreateDate(LocalDateTime.now());
    List courseBases = new ArrayList();
    courseBases.add(courseBase);
    PageResult pageResult = new PageResult(courseBases,10,1,10);
    return pageResult;

}

启动项目,打开swagger。

使用Swagger进行接口测试 :

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第10张图片

设置请求参数

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第11张图片

 swagger能看到响应的数据: 

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第12张图片

如果添加过日期格式配置类,序列化和反序列化时,时间格式就可以自定义。

5.【课程查询功能2】具体

5.1【习惯】先写持久层,再写Service

一个专业的程序员,要先写持久层,再写Service,提高代码复用性。

5.2【内容-接口模块】yml配置

仅接口模块需要写启动类和bootstrap.yml。 

server:
  servlet:
    context-path: /content
  port: 63040
#微服务配置
spring:
  application:
    name: content-api
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.101.65:3306/xc402_content?serverTimezone=UTC&userUnicode=true&useSSL=false&
    username: root
    password: mysql
# 日志文件配置路径
logging:
  config: classpath:log4j2-dev.xml

swagger:
  title: "学成在线内容管理系统"
  description: "内容系统管理系统对课程相关信息进行管理"
  base-package: com.xuecheng.content
  enabled: true
  version: 1.0.0

5.3 创建审核状态数据库字典

为什么要创建数据库字典?

降低耦合性,例如将课程的审核状态写成202001等数字,然后用数据库字典查询此数字代表的含义,这样以后想改审核状态的描述时候,就只需要从数据库字典改,不用sql语句逐条改课程表。

课程表的教学模式、审核状态、课程发布状态都将使用数据库字典:

课程审核状态的定义: 

[
    {"code":"202001","desc":"审核未通过"},
    {"code":"202002","desc":"未审核"},
    {"code":"202003","desc":"审核通过"}
]

创建存放“数据库字典”的数据库:

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第13张图片

创建dictionary表:

 学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第14张图片

5.4【业务】条件查询课程列表

package com.xuecheng.content.service.impl;

import ...

/**
 * @description 课程信息管理业务接口实现类
 * @author Mr.M
 * @date 2022/9/6 21:45
 * @version 1.0
 */
@Service
public class CourseBaseInfoServiceImpl  implements CourseBaseInfoService {


 @Autowired
 CourseBaseMapper courseBaseMapper;

 @Override
 public PageResult queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto queryCourseParamsDto) {


  //构建查询条件对象
  LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
  //构建查询条件,根据课程名称查询
     queryWrapper.like(StringUtils.isNotEmpty(queryCourseParamsDto.getCourseName()),CourseBase::getName,queryCourseParamsDto.getCourseName());
  //构建查询条件,根据课程审核状态查询
     queryWrapper.eq(StringUtils.isNotEmpty(queryCourseParamsDto.getAuditStatus()),CourseBase::getAuditStatus,queryCourseParamsDto.getAuditStatus());
//构建查询条件,根据课程发布状态查询
//todo:根据课程发布状态查询 

  //分页对象
  Page page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize());
  // 查询数据内容获得结果
  Page pageResult = courseBaseMapper.selectPage(page, queryWrapper);
  // 获取数据列表
  List list = pageResult.getRecords();
  // 获取数据总数
  long total = pageResult.getTotal();
  // 构建结果集
  PageResult courseBasePageResult = new PageResult<>(list, total, pageParams.getPageNo(), pageParams.getPageSize());
  return courseBasePageResult;


 }


}

5.5【接口】查询课程列表

com.xuecheng.content.api.CourseBaseInfoController
 @ApiOperation("课程查询接口")
@PostMapping("/course/list")
 public PageResult list(PageParams pageParams, @RequestBody QueryCourseParamsDto queryCourseParams){
     PageResult pageResult = courseBaseInfoService.queryCourseBaseList(pageParams, queryCourseParams);
    return pageResult;
 }

5.6 swagger测试

http://localhost:63040/content/swagger-ui.html

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第15张图片

5.7【idea插件】Httpclient测试

swagger测试的缺点:刷新页面后请求数据就没了,Httpclient就没有这个缺点。

2022版之后都是自带的。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第16张图片

测试步骤:

进入controller类,找到http接口对应的方法

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第17张图片

点击Generate request in HTTP Client即可生成的一个测试用例。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第18张图片

可以看到自己生成了一个.http结尾的文件

添加请求参数进行测试、运行

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第19张图片

观察控制台,测试通过。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第20张图片

5.8 封装用来Httpclient测试的文件夹

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第21张图片

xc-content-api.http

### 查询课程信息
POST {{content_host}}/content/course/list?pageNo=1&pageSize=2
Content-Type: application/json

{
  "auditStatus": "202004",
  "courseName": "java",
  "publishStatus":""
}

### 查询课程分类
GET {{content_host}}/content/course-category/tree-nodes

### 新增课程
POST {{content_host}}/content/course
Content-Type: application/json

{
  "charge": "201001",
  "price": 10,
  "originalPrice":100,
  "qq": "22333",
  "wechat": "223344",
  "phone": "13333333",
  "validDays": 365,
  "mt": "1-1",
  "st": "1-1-1",
  "name": "",
  "pic": "fdsf",
  "teachmode": "200002",
  "users": "初级人员",
  "tags": "tagstagstags",
  "grade": "204001",
  "description": "java网络编程高级java网络编程高级java网络编程高级"
}

http-client.env.json

{
  "dev": {
    "access额_token": "",
    "gateway_host": "localhost:63010",
    "content_host": "localhost:63040",
    "system_host": "localhost:63110",
    "media_host": "localhost:63050",
    "search_host": "localhost:63080",
    "auth_host": "localhost:63070",
    "checkcode_host": "localhost:63075",
    "learning_host": "localhost:63020"
  }
}

5.9 使用idea启动前端项目

前后端联调:

通常由后端工程师将接口设计好并编写接口文档,将接口文档交给前端工程师,前后端的工程师就开始并行开发,前端开发人员会使用mock数据(假数据)进行开发,当前后端代码完成后开始进行接口联调,前端工程师将mock数据改为请求后端接口获取,前端代码请求后端服务测试接口是否正常,这个过程是前后端联调。 

使用idea前后端联调:

也可以用vscode运行前端项目,这里介绍使用idea前后端联调。

1、安装nodejs

2、前端项目放到项目总目录下

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第22张图片

3、idea配置nodejs

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第23张图片

vue-cli项目的package.json就相当于maven的pom,node-modules相当于maven的本地仓库。 

4、右键点击project-xczx2-portal-vue-ts目录下的package.json文件,

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第24张图片

5、点击Show npm Scripts打开npm窗口

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第25张图片

6、点击“Edit 'serve'” setting,下边对启动项目的一些参数进行配置,选择nodejs、npm。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第26张图片

7、右键点击Serve,点击“Run serve”启动工程。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第27张图片

出现如下访问链接说明启动成功

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第28张图片

8、访问http://localhost:8601即可访问前端工程。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第29张图片

如果存在问题尝试配置serve:

1、cmd进入工程根目录

2、运行以下命令

npm install -g cnpm --registry=https://registry.npm.taobao.org

cnpm i

npm run serve

5.10 创建系统模块

前端工程报错如下:

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第30张图片

http://localhost:8601/system/dictionary/all 指向的是系统管理服务。此链接正是在前端请求后端获取数据字典数据的接口地址。数据字典表中配置了项目用的字典信息,把它放到内存中以便使用,此接口是查询字典中的全部数据

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第31张图片

Service模块application.yml:

spring:
  application:
    name: system-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.101.65:3306/xc402_system?serverTimezone=UTC&userUnicode=true&useSSL=false&
    username: root
    password: mysql
# 日志文件配置路径
logging:
  config: classpath:log4j2-dev.xml


api模块bootstrap.yml

server:
  servlet:
    context-path: /system
  port: 63110
#微服务配置
spring:
  application:
    name: system-service

# 日志文件配置路径
logging:
  config: classpath:log4j2-dev.xml

# swagger 文档配置
swagger:
  title: "学成在线内容管理系统"
  description: "内容系统管理系统对课程相关信息进行业务管理数据"
  base-package: com.xuecheng.content
  enabled: true
  version: 1.0.0

启动系统管理服务,启动成功,在浏览器请求:http://localhost:63110/system/dictionary/all

此时请求状态码不再是500,而是跨域问题:

 

5.11 解决CORS跨域问题

CORS全称是 cross origin resource share 表示跨域资源共享。

出这个提示的原因是基于浏览器的同源策略,去判断是否跨域请求,同源策略是浏览器的一种安全机制,从一个地址请求另一个地址,如果协议、主机、端口三者全部一致则不属于跨域,否则有一个不一致就是跨域请求。

5.11.1 方法一(使用):系统模块-通过cors过滤器,添加跨域响应头 

服务端在响应头添加 Access-Control-Allow-Origin:*

实现: 

系统模块的api工程config包下编写GlobalCorsConfig.java,

//这个包别导错了,有一个很像的。
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Configuration
public class CorsConfiguration{
 
    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
 
        CorsConfiguration corsConfiguration= new CorsConfiguration();
        //1、配置跨域
        // 允许跨域的请求头
        corsConfiguration.addAllowedHeader("*");
        // 允许跨域的请求方式
        corsConfiguration.addAllowedMethod("*");
        // 允许跨域的请求来源
        corsConfiguration.addAllowedOriginPattern("*");
//注释的这句会报错。因为当allowCredentials为真时,allowedorigin不能包含特殊值"*",因为不能在"访问-控制-起源“响应头中设置该值。
        //corsConfiguration.addAllowedOrigin("*");//这句会报错,具体看下文
        // 是否允许携带cookie跨域
        corsConfiguration.setAllowCredentials(true);
 
        // 任意url都要进行跨域配置,两个*号就是可以匹配包含0到多个/的路径
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
 
    }
}

 坑点:

报错:When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.

分析:当allowCredentials为真时,allowedorigin不能包含特殊值"*",因为不能在"访问-控制-起源“响应头中设置该值。

解决:

移除这条语句:

corsConfiguration.addAllowedOrigin("*");

设置允许跨域的请求来源为具体的域名端口:

corsConfiguration.addAllowedOrigin("http://localhost:8001");

或者:

corsConfiguration.addAllowedOriginPattern("*");

备用:

package com.xuecheng.system.config;
 @Configuration
 public class GlobalCorsConfig {

  /**
   * 允许跨域调用的过滤器
   */
  @Bean
  public CorsFilter corsFilter() {
   CorsConfiguration config = new CorsConfiguration();
   //允许白名单域名进行跨域调用
   config.addAllowedOrigin("*");
   //允许跨越发送cookie
   config.setAllowCredentials(true);
   //放行全部原始头信息
   config.addAllowedHeader("*");
   //允许所有请求方法跨域调用
   config.addAllowedMethod("*");
   UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
   source.registerCorsConfiguration("/**", config);
   return new CorsFilter(source);
  }
 }

5.11.2 方法二:实现接口WebMvcConfigurer

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

5.11.3 方法三:JSONP

通过script标签的src属性进行跨域请求,如果服务端要响应内容则首先读取请求参数callback的值,callback是一个回调函数的名称,服务端读取callback的值后将响应内容通过调用callback函数的方式告诉请求方。如下图:

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第32张图片

5.11.4 方法四:使用nginx反向代理为同一域

使用Nginx反向代理,不同地址、端口都被同一个域名反向代理了,这就是统一域了。这种方法在开发时没法用,所以不采用。 

由于服务端之间没有跨域,浏览器通过nginx去访问跨域地址。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第33张图片

1)浏览器先访问http://192.168.101.10:8601 nginx提供的地址,进入页面

2)此页面要跨域访问http://192.168.101.11:8601 ,不能直接跨域访问http://www.baidu.com:8601  ,而是访问nginx的一个同源地址,比如:http://192.168.101.11:8601/api ,通过http://192.168.101.11:8601/api 的代理去访问http://www.baidu.com:8601。

这样就实现了跨域访问。

浏览器到http://192.168.101.11:8601/api 没有跨域

nginx到http://www.baidu.com:8601通过服务端通信,没有跨域。

5.12前后端联调

体会前后端联调的流程,测试的功能为课程查询功能。

1、修改前端配置文件

前端默认连接的是项目的网关地址,由于现在网关工程还没有创建,这里需要更改前端工程的参数配置文件 ,修改网关地址为内容管理服务的地址。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第34张图片

网关端口63010,配置网关后就改成显示第一行,注释第三行。

2、启动前端工程,再启内容管理服务端。

进入课程管理:http://localhost:8601/#/organization/course-list

3、正常显示

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第35张图片

如果页面没正常显示,复制并格式化响应数据,进行分析。

学成在线笔记+踩坑(2)——【内容模块】课程基础查询,swagger+数据库字典+Httpclient+跨域_第36张图片

你可能感兴趣的:(数据库,mybatis,java)