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

 

posted @ 2019-09-09 18:00  雨后观山色  阅读(828)  评论(0编辑  收藏  举报