客户端两种调用方式
SPI支持两种调用方式:Spring自动注入
和 REST请求
。
对于以下SPI:
@FeignClient(“xxx-server”)
public interface XXXSpi{
@LoginNeedless
@RequestMapping(value="/v1/bizcircles",
method=RequestMethod.GET,params="districtId")
List<Bizcircle>findByDistrictIdV1(@RequestParam("districtId")int districtId);
}
客户端既可以依赖:xxx-spi-1.0.0.jar,然后当做普通Spring Bean注入:
public class ClientXXService{
@Autowired
private XXXSpi xxxSpi;
public void doStufff(){
//some logic
List<District> districts= xxxSpi.findByDistrictIdV1(20000);
// some logic
}
}
也可以使用任何HttpClient发起Http请求:
GET http://api.route.dooioo.org/xxx/server/v1/bizcircles?districtId=20000
或者Web页面ajax请求(访问API网关):
$.get(“http://api.route.dooioo.org/xxx/server/v1/bizcircles?
districtId=”+200000,function(data){
console.log(data);
});
众所周知,Ajax请求必然会涉及跨域,我们的API网关已解决了此问题。
服务划分三个角色
下图演示了微服务的开发流程: 产品 - 拆分服务 - 服务实现 - 客户端/Web UI
具体到某个服务,则划分为三个角色:
Service SPI
服务声明Service Server
服务提供者Client/UI
服务调用方,包括FeignClient、Ajax、HttpClient。
三个概念
@LoginNeedless
SPI接口默认限制是用户登录后才能调用,手动添加@LoginNeedless
以表明此接口可公开访问,无须校验用户是否登录。
假设我们有如下SPI:
@FeignClient("loupan-server")
public interface CitySpi{
@RequestMapping(value="/v1/citys",method=RequestMethod.GET)
List<City> findAllV1();
}
如果通过API网关访问:
GET http://api.route.dooioo.org/loupan/server/v1/citys
SPI提供方将直接响应401,因为此接口必须是登陆用户请求的。
正确的请求流程如下:
1,客户端首先向API网关申请登陆Token:
POST http://api.route.dooioo.org/v1/client/token?userCode=87812
&password=md5(s0x032.3)&credential=md5&companyId=1
如果工号和密码正确,响应数据如下:
{
"userCode": 87812,
"companyId": 1,
"token": "1032ace-300234b-2aedf45-32dcef"
}
2,客户端添加自定义Request Header: X-Token
,发送实际请求。
GET http://api.route.dooioo.org/loupan/server/v1/citys
X-Token: 1032ace-300234b-2aedf45-32dcef
3,API网关根据X-Token
查找登陆用户,将用户信息放在Request Header
里,转发请求给后端节点:
GET http://loupan-server-node:port/v1/citys
X-Login-CompanyId: 1
X-Login-UserCode: 87812
X-Login-Token: 1032ace-300234b-2aedf45-32dcef
4,后端节点检查X-Login-CompanyId
、X-Login-UserCode
、X-Login-Token
是否合法,不合法则直接响应401
,否则正常响应。
如果接口业务需要登陆用户的工号或公司ID,则方法声明时,可添加需要的参数。
@FeignClient("loupan-server")
public interface CitySpi{
@RequestMapping(value="/v1/citys",method=RequestMethod.GET)
List<City> findAllV1(
@RequestHeader(StandardHttpHeaders.X_Login_UserCode)int loginUserCode);
}
@FeignClient("loupan-server")
public interface CitySpi{
@RequestMapping(value="/v1/citys",method=RequestMethod.GET)
List<City> findAllV1(
@RequestHeader(StandardHttpHeaders.X_Login_UserCode)int loginUserCode,
@RequestHeader(StandardHttpHeaders.X_Login_CompanyId)int loginCompanyId);
}
如果你的接口不涉及敏感业务数据,允许公开访问,则可以手动添加@LoginNeedless
,表明此接口无需登陆,无需申请X-Token
,客户端可直接访问。
@FeignClient("loupan-server")
public interface CitySpi{
@LoginNeedless
@RequestMapping(value="/v1/citys",method=RequestMethod.GET)
List<City> findAllV1();
}
注意:如果通过Spring Autowired
的方式调用SPI,目前不进行任何登陆校验,默认是信任的。
@LorikRest
上文提到,我们SPI有两种调用方式:Spring自动注入
和REST请求
。
@FeignClient("loupan-server")
public interface CitySpi{
@RequestMapping(value="/v1/citys/{id}",method=RequestMethod.GET)
City findByIdV1(@PathVariable(value="id")int id);
}
如果findByIdV1
返回null
,那么对于普通的JAVA调用来说就是记录不存在的意思,但是,对于REST请求来说,404
才符合这个语义。
LorikRest
注解是为了让SPI接口兼容REST访问而存在的。它提供了一些特性,
目前支持: Feature.NullTo404
(如果方法返回null,则统一响应404),可以指定方法的业务码。
@FeignClient("loupan-server")
public interface CitySpi{
@LorikRest(value={Feature.NullTo404},codes={21000})
@RequestMapping(value="/v1/city/{id}",method=RequestMethod.GET)
City findByIdV1(@PathVariable(value="id")int id);
}
如果你的SPI暂无客户端REST访问,也可以不添加此标注。
@summary
@summary
是我们约定的Java Doc Tag,用于方法或类注释中,是功能简介,并不是代码的一部分。@summary
最好20字
以内,以简要概括类或方法的功能,便于API网关生成文档。
/**
* 城市标准服务,目前仅支持上海、苏州。
* @summary 城市
* @Copyright (c) 2016, Lianjia Group All Rights Reserved.
*/
@FeignClient("loupan-server")
public interface CitySpi{
/**
* 中国大陆的每个城市都有一个国标码(GB/T 2260-2002),比如北京:110000,上海
* 310000,推荐外键使用国标码来标识一个城市,而不要使用City#getId。
* @author huisman
* @version v1
* @param gbCode 国标码
* @return 城市
* @since 2016-01-01
* @summary 根据国标码查询城市
*/
@LorikRest(value={Feature.NullTo404},codes={21000})
@RequestMapping(value="/v1/city/{gbCode}",method=RequestMethod.GET)
City findByGbCodeV1(@PathVariable(value="gbCode")int gbCode);
}