当前位置: 首页 > news >正文

商城项目07_网关工程初步搭建、商品分类树状结构展示、网关配置、解决跨域问题

文章目录

  • ①. 网关工程初步搭建
  • ②. 拼装获取树形菜单结构
  • ③. 将renren-fast、product项目与nacaos结合
  • ④. 前端页面如何新增商品系统分类维护
  • ⑤. 解决跨域问题 掌握
  • ⑥. 配置product网关、前端展示

①. 网关工程初步搭建

  • ①. 搭建的工程如图所示,并在gateway服务中开启注册服务发现@EnableDiscoveryClient
    在这里插入图片描述
@EnableDiscoveryClient
//由于我们在common工程里面引入了数据库的依赖,网关工程中没有进行配置,这里将其忽略,否则会报错误信息
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class GulimallGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GulimallGatewayApplication.class, args);
    }
}
  • ②. 在nacos配置中心中配置如下文件
    在这里插入图片描述在这里插入图片描述

  • ③. bootstrap.yml

spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: 3fa6c8bc-2d25-4824-9773-ca6e14a42308
        file-extension: yaml
  application:
    name: gulimall-gateway
  • ④. application.yaml
spring:
  cloud:
    gateway:
      routes:
        - id: test_route1
          uri: http://localhost:7000
          predicates:
            - Path=/coupon/coupon/test/**
  • ⑤. 测试:当我们访问 http://localhost:88/coupon/coupon/test 会在网关进行路径匹配后进行跳转到coupon优惠卷服务的controller
    在这里插入图片描述

②. 拼装获取树形菜单结构

  • ①. 数据库表设置
    在这里插入图片描述
CREATE TABLE `pms_category` (
  `cat_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类id',
  `name` char(50) DEFAULT NULL COMMENT '分类名称',
  `parent_cid` bigint(20) DEFAULT NULL COMMENT '父分类id',
  `cat_level` int(11) DEFAULT NULL COMMENT '层级',
  `show_status` tinyint(4) DEFAULT NULL COMMENT '是否显示[0-不显示,1显示]',
  `sort` int(11) DEFAULT NULL COMMENT '排序',
  `icon` char(255) DEFAULT NULL COMMENT '图标地址',
  `product_unit` char(50) DEFAULT NULL COMMENT '计量单位',
  `product_count` int(11) DEFAULT NULL COMMENT '商品数量',
  PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1433 DEFAULT CHARSET=utf8mb4 COMMENT='商品三级分类';
  • ②. 导入数据(将sql脚本导入进来)

  • ③. 使用java8新特性查找三级联洞并封装成树形结构

@RestController
@RequestMapping("product/category")
public class CategoryController {
    @Autowired
    private CategoryService categoryService;
    /**
     * 查出所有分类、以及子分类,以树形结构组装起来
     */
    @RequestMapping("/list/tree")
    //@RequiresPermissions("product:category:list")
    public R list(){
        List<CategoryEntity> entities = categoryService.listWithTree();
        return R.ok().put("data", entities);
    }
}
    @Override
    public List<CategoryEntity> listWithTree() {
        //1、查出所有分类
        List<CategoryEntity> entities = baseMapper.selectList(null);
        //2、组装成父子的树形结构
        //2.1)、找到所有的一级分类
        List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity ->
                categoryEntity.getParentCid() == 0
        ).map((menu)->{
            menu.setChildren(getChildrens(menu,entities));
            return menu;
        }).sorted((menu1,menu2)->{
            return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());

        return level1Menus;
    }

    //递归查找所有菜单的子菜单
    private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){

        List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
            return categoryEntity.getParentCid() == root.getCatId();
        }).map(categoryEntity -> {
            //1、找到子菜单
            categoryEntity.setChildren(getChildrens(categoryEntity,all));
            return categoryEntity;
        }).sorted((menu1,menu2)->{
            //2、菜单的排序
            return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());

        return children;
    }
@Data
@TableName("pms_category")
public class CategoryEntity implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 * 分类id
	 */
	@TableId
	private Long catId;
	/**
	 * 分类名称
	 */
	private String name;
	/**
	 * 父分类id
	 */
	private Long parentCid;
	/**
	 * 层级
	 */
	private Integer catLevel;
	/**
	 * 是否显示[0-不显示,1显示]
	 */
	@TableLogic(value = "1",delval = "0")
	private Integer showStatus;
	/**
	 * 排序
	 */
	private Integer sort;
	/**
	 * 图标地址
	 */
	private String icon;
	/**
	 * 计量单位
	 */
	private String productUnit;
	/**
	 * 商品数量
	 */
	private Integer productCount;
	@TableField(exist = false)
	private List<CategoryEntity>children;
}
  • ④. 结果测试 http://localhost:10001/product/category/list/tree
    在这里插入图片描述

③. 将renren-fast、product项目与nacaos结合

  • ①. 在renren-fast导入公共依赖,并在主启动类上加上@EnableDiscoveryClient
@EnableDiscoveryClient
@SpringBootApplication
public class RenrenApplication {
	public static void main(String[] args) {
		SpringApplication.run(RenrenApplication.class, args);
	}
  • ②. 在配置文件中将reren-fast注入到注册中心nacos、由于在common工程中导入了nacos的配置文件,这里也将config进行配置
spring:
  application:
    name: renren-fast
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
# bootstrap.properties
spring.application.name=renren-fast
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

在这里插入图片描述

④. 前端页面如何新增商品系统分类维护

  • ①. 启动renren-fast和renren-fast-vue,登录系统后新增分类维护菜单栏
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

  • ②. 通过角色管理role,看到url的路径是sys/role,在浏览器上将/变成了-(sys/role),打开vscode可以看到关系如下:在这里插入图片描述

  • ③. 在vscode中新建product文件夹,新建category.vue

<template>
  <div class="">
    <el-tree
      :data="data"
      :props="defaultProps"
      @node-click="handleNodeClick"
    ></el-tree>
  </div>
</template>

<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';

export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {};
  },
  //监听属性 类似于data概念
  computed: {},
  data() {
    return {
      data: [],
      defaultProps: {
        children: "children",
        label: "label"
      }
    };
  },
  methods: {
    handleNodeClick(data) {
      console.log(data);
    },
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get"
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.data = data.data;
        } else {
          this.dataList = [];
        }
      });
    }
  },
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {
    this.getMenus();
  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {}
};
</script>
<style scoped></style>
  • ④. 规定以后前端发送来的请求都经过网关,并且带上api路径

在这里插入图片描述

  • ⑤. 配置gateway网关设置,admin_route
    如果我们在http://localhost:8001/#/login 这样的方式进行登录会出现跨域问题
spring:
  cloud:
    gateway:
      routes:
        - id: test_route1
          uri: http://localhost:7000
          predicates:
            - Path=/coupon/coupon/test/**
        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
# 前端项目 /api
# http://localhost:88/api/captcha.jpg?uuid=72b9da67-0130-4d1d-8dda-6bfe4b5f7935
# http://renren-fast:8080/renren-fast/captcha.jpg

在这里插入图片描述

⑤. 解决跨域问题 掌握

  • ①. 登录,还是报错:(出现了跨域的问题,就是说vue项目是8001端口,却要跳转到88端口,为了安全性,不可以)
    8001/#/login:1 Access to XMLHttpRequest at ‘http://localhost:88/api/sys/login’ from origin ‘http://localhost:8001’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
    从8001访问88,引发CORS跨域请求,浏览器会拒绝跨域请求。具体来说当前页面是8001端口,但是要跳转88端口,这是不可以的(post请求json可以)

在这里插入图片描述

  • ②. 什么是跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js施加的安全限制(ajax可以)

  • ③. 同源策略:是指协议,域名,端囗都要相同,其中有一个不同都会产生跨域
    在这里插入图片描述

  • ④. 跨域流程:这个跨域请求的实现是通过预检请求实现的,先发送一个OPSTIONS探路,收到响应允许跨域后再发送真实请求

跨域请求流程:
非简单请求(PUT、DELETE),需要先发送预检请求


       -----1、预检请求、OPTIONS ------>
       <----2、服务器响应允许跨域 ------
浏览器 |                               |  服务器
       -----3、正式发送真实请求 -------->
       <----4、响应数据   --------------

在这里插入图片描述

  • ⑤. 跨域的解决方案
  1. 设置nginx包含admin和gateway。都先请求nginx,这样端口就统一了
  2. 让服务器告诉预检请求能跨域(我们的项目使用第二种方式解决问题)

在这里插入图片描述

  • ⑥. 我们在网关的配置如下内容进行解决跨域问题(添加响应头)
  1. Access-Control-Allow-Origin:支持哪些来源的请求跨域
  2. Access-Control-Allow-Methods:支持哪些方法跨域
  3. Access-Control-Allow-Credentials:跨域请求默认不包含cookie,设置为true可以包含 cookie
  4. Access-Control-Expose-Headers:跨域请求暴露的字段(CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段: Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定)
  5. Access-Control-Max-Age:表明该响应的有效时间为多少秒。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果 该首部字段的值超过了最大有效时间,将不会生效
package com.atguigu.gulimall.gateway.config;
@Configuration // gateway
public class GulimallCorsConfiguration {
    @Bean // 添加过滤器
    public CorsWebFilter corsWebFilter(){
        // 基于url跨域,选择reactive包下的
        UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
        // 跨域配置信息
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许跨域的头
        corsConfiguration.addAllowedHeader("*");
        // 允许跨域的请求方式
        corsConfiguration.addAllowedMethod("*");
        // 允许跨域的请求来源
        corsConfiguration.addAllowedOrigin("*");
        // 是否允许携带cookie跨域
        corsConfiguration.setAllowCredentials(true);

        // 任意url都要进行跨域配置
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}
  • ⑦. 再次访问:http://localhost:8001/#/login
    同源策略禁止读取:http://localhost:88/api/sys/login的远程资源(原因:不允许多个CORS头),在renren-fast也配置了跨域,需要将renren-fast中的跨域也注释掉
    在这里插入图片描述在这里插入图片描述

⑥. 配置product网关、前端展示

  • ①. 这个时候去访问在页面访问:http://localhost:88/api/product/category/list/tree可以显示数据
    (在product的配置文件中配置注册中心和配置中心的配置文件)
#  如下的配置在nacos中也进行了配置(从spring 到 port: 88),可以进行注释掉
#  当一个请求过来的时候,会交给网关进行处理,通过一系列的预言,如果预言为真,会转到指定的服务,转到指定服务的时候,会经过一系列的过滤器
spring:
#  application:
#    name: gulimall-gateway
#  cloud:
#    nacos:
#      discovery:
#        server-addr: 127.0.0.1:8848
#server:
#  port: 88
  cloud:
    gateway:
      routes:
        # 测试网关的功能
        - id: test_route1
          uri: http://localhost:7001
          predicates:
            - Path=/coupon/coupon/nacos/config/simple/**
        # 商品服务网关配置(注意需要把product_route放在admin_route前面)
        # http://localhost:88/api/product/category/list/tree
        # http://localhost:10000/product/category/list/tree
        - id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>/?.*), /$\{segment}
        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
# 前端项目 /api
# http://localhost:88/api/captcha.jpg?uuid=72b9da67-0130-4d1d-8dda-6bfe4b5f7935
# http://renren-fast:8080/renren-fast/captcha.jpg

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • ②. 设置前端代码,在页面展示树状结构查询
<!--  -->
<template>
  <div class="">
    <el-tree
      :data="menus"
      :props="defaultProps"
      @node-click="handleNodeClick"
    ></el-tree>
  </div>
</template>

<script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';

export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {};
  },
  //监听属性 类似于data概念
  computed: {},
  data() {
    return {
      menus: [],
      defaultProps: {
        children: "children",
        label: "name"
      }
    };
  },
  methods: {
    handleNodeClick(data) {
      console.log(data);
    },
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get"
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.menus = data.data;
        }
      });
    }
  },
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {
    this.getMenus();
  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {}
};
</script>
<style scoped></style>

在这里插入图片描述

相关文章:

  • 【Python刷题篇】——Python入门 09 字典(下)
  • MySQL-查询数据库(二)
  • 《安富莱嵌入式周报》第281期:Keil Studio发布VSCode插件,微软嵌入式IDE升级,开源穿戴手表,CAN XL汽车单片机,USB4 V2.0规范
  • 【数据挖掘】pandas使用手册
  • 图像处理技术的综合应用——提取篮球
  • 2021 第四届 浙江省大学生网络与信息安全竞赛技能赛 预赛 Writeup,4题
  • 【MCAL_CANDriver】-1.2-Can Mailbox邮箱,Hardware Object,HOH,HRH,HTH之间的关系
  • 【零基础学QT】第七章 网络通信,TCP、UDP通信实验
  • 一次解释器模式的实际使用
  • C++入门·收尾
  • 25.CF992E Nastya and King-Shamans 转化+线段树二分
  • 快来带您了解中秋节的前世今生
  • 分布式锁之防止超卖 --mysql原子操作,乐观锁,redis事务,乐观锁
  • 【算法刷题】第一篇——哈希
  • 小脚本杂文shell脚本
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • Bootstrap JS插件Alert源码分析
  • ES6语法详解(一)
  • JDK9: 集成 Jshell 和 Maven 项目.
  • MQ框架的比较
  • Python连接Oracle
  • python学习笔记-类对象的信息
  • Sass 快速入门教程
  • ubuntu 下nginx安装 并支持https协议
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 排序(1):冒泡排序
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 一些关于Rust在2019年的思考
  • 原生 js 实现移动端 Touch 滑动反弹
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • 阿里云ACE认证学习知识点梳理
  • 带你开发类似Pokemon Go的AR游戏
  • 数据库巡检项
  • ​第20课 在Android Native开发中加入新的C++类
  • #每日一题合集#牛客JZ23-JZ33
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (70min)字节暑假实习二面(已挂)
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (超详细)语音信号处理之特征提取
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)Linux下编译安装log4cxx
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET Project Open Day(2011.11.13)
  • .NET 设计一套高性能的弱事件机制
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET运行机制
  • /etc/fstab 只读无法修改的解决办法
  • []新浪博客如何插入代码(其他博客应该也可以)