商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息,我们需要依次去完成:商品分类、品牌、商品的开发。
1.导入数据库表
打开Navicat软件,选择对应的数据库,运行sql文件。
执行结果:
2.
根据这个路由路径到路由文件(src/route/index.js),可以定位到分类管理页面:
由路由文件知,页面是src/pages/item/Category.vue
商品分类使用了树状结构,而这种结构的组件vuetify并没有为我们提供,这里自定义了一个树状组件。不要求实现或者查询组件的实现,只要求可以参照文档使用该组件即可:
(1)url异步请求
http://manage.leyou.com/item/category/list
但实际却是:
http://api.leyou.com/api/item/category/list?pid=0
这就会出现跨域问题(第3大点分析)
这是因为,我们有一个全局的配置文件,对所有的请求路径进行了约定:
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; } }
GeneratedValue
<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> { }
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
-
请求方式:决定我们用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)启动并测试
<3>刷新后台管理页面查看:
这其实是浏览器的同源策略造成的跨域问题。
3.
以下情况都属于跨域:
如果域名和端口都相同,但是请求路径不同,不属于跨域,如:
www.jd.com/item
www.jd.com/goods
http和https也属于跨域
而我们刚才是从manage.leyou.com
去访问api.leyou.com
因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。
因此:跨域问题 是针对ajax的一种限制。
优势:
-
在服务端进行控制是否允许跨域,可自定义规则
-
它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
-
浏览器端:
-
服务端:
<2>CORS解决跨域实现
SpringMVC已经帮我们写好了CORS的跨域过滤器: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)使用域名访问效果图