• 018 实现商品分类查询以及使用域名访问项目的启动步骤


    商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息,我们需要依次去完成:商品分类、品牌、商品的开发。

    1.导入数据库表

    打开Navicat软件,选择对应的数据库,运行sql文件。

    执行结果:

    2.实现功能

    在浏览器页面点击“分类管理”菜单:

    根据这个路由路径到路由文件(src/route/index.js),可以定位到分类管理页面:

    由路由文件知,页面是src/pages/item/Category.vue

    商品分类使用了树状结构,而这种结构的组件vuetify并没有为我们提供,这里自定义了一个树状组件。不要求实现或者查询组件的实现,只要求可以参照文档使用该组件即可:

    (1)url异步请求

    点击商品管理下的分类管理子菜单,在浏览器控制台可以看到:

     

    我们明明是使用的相对路径:/item/category/list,讲道理发起的请求地址应该是:

    http://manage.leyou.com/item/category/list

    但实际却是:

    http://api.leyou.com/api/item/category/list?pid=0

    这就会出现跨域问题(第3大点分析)

    这是因为,我们有一个全局的配置文件,对所有的请求路径进行了约定:

     

    路径是http://api.leyou.com,并且默认加上了/api的前缀,这恰好与我们的网关设置匹配,我们只需要把地址改成网关的地址即可,因为我们使用了nginx反向代理,这里可以写域名。

    (2)实体类

    leyou-item-interface中添加category实体类:

     

    package lucky.leyou.item.domain;
    
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Table(name="tb_category")
    public class Category {
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private Long id;
        private String name;
        private Long parentId;
        // 注意isParent生成的getter和setter方法需要手动加上Is
        //实际开发中尽量避免数据库字段名以is开头
        private Boolean isParent; 
        private Integer sort;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Long getParentId() {
            return parentId;
        }
    
        public void setParentId(Long parentId) {
            this.parentId = parentId;
        }
    
        public Boolean getIsParent() {
            return isParent;
        }
    
        public void setIsParent(Boolean parent) {
            isParent = parent;
        }
    
        public Integer getSort() {
            return sort;
        }
    
        public void setSort(Integer sort) {
            this.sort = sort;
        }
    }

    需要注意的是,这里要用到jpa的注解(即实体类上所加的注解@Table、@Id、@GeneratedValue),因此我们在leyou-item-interface模块中的pom文件添加jpa依赖

    <dependencies>
            <dependency>
                <groupId>javax.persistence</groupId>
                <artifactId>persistence-api</artifactId>
                <version>1.0</version>
            </dependency>
        </dependencies>

    (3)mapper

    我们使用通用mapper来简化开发:

    package lucky.leyou.item.mapper;
    
    import lucky.leyou.item.domain.Category;
    import tk.mybatis.mapper.common.Mapper;
    
    public interface CategoryMapper extends Mapper<Category> {
    }

    要注意,我们并没有在mapper接口上声明@Mapper注解,那么mybatis如何才能找到接口呢?

    我们在启动类上添加一个扫描包功能:

    package lucky.leyou;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import tk.mybatis.spring.annotation.MapperScan;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @MapperScan("lucky.leyou.item.mapper")   // mapper接口的包扫描
    public class LeyouItemServiceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(LeyouItemServiceApplication.class, args);
        }
    }

    (4)service

    一般service层我们会定义接口和实现类。

    接口:

    package lucky.leyou.item.service;
    
    import lucky.leyou.item.domain.Category;
    
    import java.util.List;
    
    public interface ICategoryService {
        /**
         * 根据parentId查询子类目
         * @param pid
         * @return
         */
        public List<Category> queryCategoriesByPid(Long pid);
    }

    实现类:

    package lucky.leyou.item.service.impl;
    
    import lucky.leyou.item.domain.Category;
    import lucky.leyou.item.mapper.CategoryMapper;
    import lucky.leyou.item.service.ICategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class CategoryServiceImpl implements ICategoryService {
    
        @Autowired
        private CategoryMapper categoryMapper;
    
        /**
         * 根据parentId查询子类目
         * @param pid
         * @return
         */
        @Override
        public List<Category> queryCategoriesByPid(Long pid) {
            Category record = new Category();
            record.setParentId(pid);
            return this.categoryMapper.select(record);
        }
    }

    (5)Controller

    编写一个controller一般需要知道四个内容:

    • 请求方式:决定我们用GetMapping还是PostMapping

    • 请求路径:决定映射路径

    • 请求参数:决定方法的参数

    • 返回值结果:决定方法的返回值

    在刚才页面发起的请求中,我们就能得到绝大多数信息

    • 请求方式:Get,查询肯定是get请求
    • 请求路径:/api/item/category/list。其中/api是网关前缀,/item是网关的路由映射,真实的路径应该是/category/list
    • 请求参数:pid=0,根据tree组件的说明,应该是父节点的id,第一次查询为0,那就是查询一级类目

    模块结构:

    添加Controller:

    package lucky.leyou.item.controller;
    
    import lucky.leyou.item.domain.Category;
    import lucky.leyou.item.service.ICategoryService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.util.CollectionUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.List;
    
    @Controller
    @RequestMapping(path = "/category")
    public class CategoryController {
        @Autowired
        private ICategoryService iCategoryService;
    
        /**
         * 根据parentId查询子类目
         * @param pid @RequestParam(value = "pid",defaultValue = "0") long pid 作用:接收url中携带的请求参数,设置默认值为0
         * @return
         */
        @RequestMapping("/list")
        public ResponseEntity<List<Category>> queryCategoriesByPid(@RequestParam(value = "pid",defaultValue = "0") Long pid){
    
            if(pid==null||pid<0){
                //响应类型400:如果pid为null或pid<0,返回请求参数不合法
                return ResponseEntity.badRequest().build();
            }
            List<Category> categories = this.iCategoryService.queryCategoriesByPid(pid);
            //利用CollectionUtils.isEmpty(categories)判断集合是否为空
            if(CollectionUtils.isEmpty(categories)){
                //响应类型404:资源服务器未找到
                return ResponseEntity.notFound().build();
            }
            //响应类型200:查询成功
            return ResponseEntity.ok(categories);
    
        }
    
    
    }

    (6)启动并测试

    <1>我们不经过网关,直接访问:http://localhost:8081/category/list

    <2>试试网关是否畅通:http://api.leyou.com/api/item/category/list

    <3>刷新后台管理页面查看:

    这其实是浏览器的同源策略造成的跨域问题。

    3.跨域问题

    跨域:浏览器对于javascript的同源策略的限制 。

    以下情况都属于跨域:

    如果域名和端口都相同,但是请求路径不同,不属于跨域,如:

    www.jd.com/item

    www.jd.com/goods

    http和https也属于跨域

    而我们刚才是从manage.leyou.com去访问api.leyou.com,这属于二级域名不同,跨域了

    (1)跨域问题产生的场景

    跨域不一定都会有跨域问题。

    因为跨域问题是浏览器对于ajax请求的一种安全限制一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。

    因此:跨域问题 是针对ajax的一种限制

    (2)CORS解决跨域

    <1>CORS概述

    CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

    CORS是一个规范化的跨域请求解决方案,安全可靠。

    优势:

    • 在服务端进行控制是否允许跨域,可自定义规则

    • 支持各种请求方式

    它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

    • 浏览器端:

      目前,所有浏览器都支持该功能(IE10以下不行)。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。

    • 服务端:

      CORS通信与AJAX没有任何差别,因此你不需要改变以前的业务逻辑。只不过,浏览器会在请求中携带一些头信息,我们需要以此判断是否允许其跨域,然后在响应头中加入一些信息即可。这一般通过过滤器完成即可。

    <2>CORS解决跨域实现

    SpringMVC已经帮我们写好了CORS的跨域过滤器:CorsFilter ,内部已经实现了刚才所讲的判定逻辑,我们直接用就好了。

    leyou-gateway2模块中编写一个配置类,并且注册CorsFilter:

     

    代码:

    package lucky.leyou.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    /**
     * 利用cors解决跨域问题
     */
    @Configuration
    public class LeyouCorsConfig {
        @Bean
        public CorsFilter corsFilter() {
            //1.添加CORS配置信息
            CorsConfiguration config = new CorsConfiguration();
            //1) 允许的域,不要写*,否则cookie就无法使用了
            config.addAllowedOrigin("http://manage.leyou.com");
            //2) 是否发送Cookie信息
            config.setAllowCredentials(true);
            //3) 允许的请求方式
            config.addAllowedMethod("OPTIONS");
            config.addAllowedMethod("HEAD");
            config.addAllowedMethod("GET");
            config.addAllowedMethod("PUT");
            config.addAllowedMethod("POST");
            config.addAllowedMethod("DELETE");
            config.addAllowedMethod("PATCH");
            // 4)允许的头信息
            config.addAllowedHeader("*");
    
            //2.添加映射路径,我们拦截一切请求
            UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
            configSource.registerCorsConfiguration("/**", config);
    
            //3.返回新的CorsFilter.
            return new CorsFilter(configSource);
        }
    }

    重启网关模块leyou-gateway2,然后在浏览器中测试,访问正常:

    若出现leyou-gateway2模块中报如下错误com.netflix.zuul.exception.ZuulException: Forwarding error

    解决方案:将如下的3个服务全部重新启动。

    4.项目启动顺序

    (1)开启后台微服务

    (2)开启前端

    输入: npm start

    (3)Nginx反向代理解决端口问题,实现利用域名访问。

    start nginx.exe

    (4)使用域名访问效果图

    http://manage.leyou.com/#/index/dashboard

  • 相关阅读:
    CSS3动画
    Grid布局
    JS向上取整、向下取整、四舍五入等
    JS DOM资料
    关于setInterval和setTimeout中的this指向问题
    JavaScript 高级技巧 Memoization
    请求接口的方式
    HTTP协议知识
    CSS样式重置
    Chrome 为什么使用多进程,不使用多线程
  • 原文地址:https://www.cnblogs.com/luckyplj/p/11493231.html
Copyright © 2020-2023  润新知