item微服务的结构
首先介绍一下商品微服务:
因为是父工程里面嵌套的一个父工程,item里有两个服务,一个是专门处理商品业务逻辑的微服务,一个是实体类的微服务,所以实体类那个微服务不需要yml文件,不需要启动类,只需要被调用就可以。
商品业务相关
首先贴一下启动类
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.hao.item.mapper")
public class LyItemApplication {
public static void main(String[] args) {
SpringApplication.run(LyItemApplication.class);
}
}
这里除了@SpringBootApplication还有两个注解
1, @EnableDiscoveryClient 是注册自己为eureka的客户端,因为eureka是注册中心的一种实现方式,而EnableDiscoveryClient可以被其他的注册中心发现并注册(需要在yml中配置),当然在这个项目中使用@EnableEurekaClient也是可以的,不过建议使用第一种。
2, @MapperScan是扫描mapper包,项目中使用的是通用mapper,一定不要导错包。它的包是
import tk.mybatis.spring.annotation.MapperScan;
不然你会发现奇怪的错误。
yml文件
这里的driver-class-name:可以省略,他会自动判断你使用的哪种数据库。
datasource:
url: jdbc:mysql://localhost:3306/leyou
username: root
password: 123456
controller:
@RestController
@RequestMapping("category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
* 根据父节点id查询商品分类
*
* @param pid
* @return
*/
@GetMapping("list")
public ResponseEntity<List<Category>> queryCategoryListByPid(@RequestParam("pid") long pid) {
return ResponseEntity.ok(categoryService.queryCategoryListByPid(pid));
}
/**
* 根据商品分类Ids查询分类
* 使用rest风格
*
* @param ids
* @return
*/
@GetMapping("list/ids")
public ResponseEntity<List<Category>> queryCategoryByIds(@RequestParam("ids") List<Long> ids) {
return ResponseEntity.ok(categoryService.queryByIds(ids));
}
}
- 这里第一次使用@RestController,只加@Controller如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。 加@RestController如果返回jsp或者html需要return newModelAndView(“XXX”)。
- ResponseEntity<>:
@ResponseBody可以直接返回Json结果,
@ResponseEntity不仅可以返回json结果,还可以定义返回的HttpHeaders和HttpStatus,这可以为了自定义异常处理提供状态码。
service:
@Service
public class CategoryService {
@Autowired
private CategoryMapper categoryMapper;
public List<Category> queryCategoryListByPid(long pid) {
// new出来实体类,把非空属性拿出来当做查询条件
Category t = new Category();
t.setParentId(pid);
List<Category> list = categoryMapper.select(t);
// 判断结果
if (CollectionUtils.isEmpty(list)) {
throw new LyException(ExceptionEnum.CATEGORY_NOT_FOUND);
}
return list;
}
/**
* 查询AllGoods里传来的id的商品名称,查询分类
*/
public List<Category> queryByIds(List<Long> ids) {
List<Category> categories = categoryMapper.selectByIdList(ids);
// 判断结果
if (CollectionUtils.isEmpty(categories)) {
throw new LyException(ExceptionEnum.CATEGORY_NOT_FOUND);
}
return categories;
}
}
- 这里使用的通用mapper,new出来实体类,把非空属性拿出来当做查询条件
Category t = new Category();
t.setParentId(pid);
List<Category> list = categoryMapper.select(t);
- 通用mapper也可以查询集合,不过要在mapper中继承 IdListMapper<>,它还可以复杂查询,以后用到再讲。
List<Category> categories = categoryMapper.selectByIdList(ids);
mapper:
public interface CategoryMapper extends Mapper<Category>, IdListMapper<Category, Long> {
}
简单查询可以只用继承 Mapper<>,里面放入相应的实体类就好,如果使用集合需要传递实体类和主键类型。
category实体类:
import lombok.Data;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Table(name = "tb_category")
public class Category {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private String name;
private Long parentId;
private Boolean isParent;
private Integer sort;
}
- @data是lombok自动set和get方法,也可以自己写getset方法
- @table指定相应的数据库
- @Id设置该字段为主键
- @KeySql(useGeneratedKeys = true)主键自增长。
跨域问题
当我们完成上面的配置后,发现项目功能还是实现不了,那是因为有了跨域问题。
跨域是指跨域名的访问,以下情况都属于跨域:
如果域名和端口都相同,但是请求路径不同,不属于跨域,如:
而刚才是通过自己的zuul网关从 manage.leyou.com 去访问 api.leyou.com,这属于二级域名不同,跨域了。
为什么有跨域问题
跨域不一定会有跨域问题。
因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是于当前页同域名的路径,这能有效的阻止跨站攻击。
因此:跨域问题 是针对ajax的一种限制。
解决跨域问题的方案
目前比较常用的跨域解决方案有3种:
- Jsonp
最早的解决方案,利用script标签可以跨域的原理实现。
限制:- 需要服务的支持
- 只能发起GET请求
- nginx反向代理
思路是:利用nginx反向代理把跨域为不跨域,支持各种请求方式
缺点:需要在nginx进行额外配置,语义不清晰 - CORS
规范化的跨域请求解决方案,安全可靠。
优势:- 在服务端进行控制是否允许跨域,可自定义规则
- 支持各种请求方式
缺点: - 会产生额外的请求
我们这里会采用cors的跨域方案。
什么是cors
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
- 浏览器端:
目前,所有浏览器都支持该功能(IE10以下不行)。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。 - 服务端:
CORS通信与AJAX没有任何差别,因此你不需要改变以前的业务逻辑。只不过,浏览器会在请求中携带一些头信息,我们需要以此判断是否运行其跨域,然后在响应头中加入一些信息即可。这一般通过过滤器完成即可。
浏览器会将ajax请求分为两类,其处理方案略有差异:简单请求、特殊请求。
具体简单请求和特殊请求就不详细介绍,反正实现挺简单的。
在ly-api-gateway(网关微服务)中编写一个配置类,并且注册CorsFilter:
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//1) 允许的域,不要写*,否则cookie就无法使用了
config.addAllowedOrigin("http://manage.leyou.com");
config.addAllowedOrigin("http://www.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("*");
//5)有效的时长
config.setMaxAge(3600L);
//2.添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
经过一顿配置就可以跨域了。
跨域后就可以实现功能了。