通过API调用Gitlab中文件路径等问题的解决方案
一、前言
通过gitlab的API获取gitiab文件相关操作时,例如,通过API访问gitlab仓库中某个文件夹下面的文件(例:xx仓库/app/text.js)。通过gitiab官方API文档要求文件路径需进行url编码,即url中的/
编码为%2F
,.
编码为%2E
,以此和url请求路径区分开(如:projects/280 repository/files/app%2Ftext%2Ejs)
获取gitlab仓库中文档的api
GET /projects/:id/repository/files/:file_path/raw
官方文档的步骤来,使用curl
命令在控制台中测试一下,看该命令是否生效
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb/raw?ref=master"
但是,在微服务的开发中,通常会使用FeignClient
的方式来调用其他服务。
调用的时候会将文件路经flePath
参数使用@PathVariable
注解用于URL占位,filePath= app%2Ftext.js
如下所示:
GetMapping(value="/projects/{projectId}/repository/files/{file_path}/raw")
GitlabFileDTO getRawFile(@PathVariable(value = "projectid") String projectid,
@PathVariable(value = "file_path") String filePath, @RequestParam(value = "access_token") String token, @RequestParam(value = "ref") String ref);
我在feignClient
把相关接口写好之后,测试了半天,一直给我报错。一步一步debug。先是在控制台中使用curl
测试没有问题,于是怀疑是不是路径出了问题。
于是把feignClient
的调用日志打开.
FeignClient打印请求日志可以看看这里。
c语言,坑啊。
查看调用日志,打印URL为:
projects/280/repository/fles/app/text.js?ref=master
这就是说,之前给/
,.
编码,但是经过feignclient
之后,feignClient
又给我解码了。
(@RequestParam注解却不会将%2F
解码为/
,下文解释原因),因此调用这个查看文件的api会失败,通过查看Feign源码,我们很容易解决这个疑惑。
二、解决方案
Feign RequestTemplate 中 resolve 方法在构建uri时,首先处理ur中占位符参数,即@PathVariable
定义的,其中uriTemplate.expand(variables)
方法中 expanded.replaceAll("%2F","/")
会%2F
转成/
。因此路经中的%2F会自动解码。接着再解析query参数,即URL?后面通过@RequestParam定义的参数,URL再通过字符串拼接连接在一起,因此@RequestParam参数不会自动解码。
有一种简单的解决方案可以在调用Feign client
之前将/
换成%2f
(F小写),通过以上代码可知可以跳过这里的替换,但是下面提供了更好的方法解决这个问题。
通过查看源码我们可以发现,只有当this.encodeSlash=true
时会进行替换,因此通过设置encodeslash/decodeSlash
这两个标志的值可以控制url path
变量是否需要解码。控制这两个标志只有使用Feign
原生的@RequestLine
注解。
Feign默认使用的是Springmvc
注解(就是RequestMapping
之类的 ,所以需要通过新增一个配置类来修改其“契约”,以便调用Feign原生的注解。
public class GitlabFileFeignclientConfig {
@Bean
public Contract feignContract(){
return new Contract.Default();
}
配置完成后即可使用@RequestLine
注解,写法如下例所示:
@RequestLine(value="GET/projects/{projectId}/repository/files/{file_path}?access_token={access_token}&ref={ref)", decodeSlash = false)
说明:
- 请求方法GET后有空格;
- 不能使用Spring注解@RequestHeader,@PathVariable,@RequestParam等,可使用Feign注解@Param,@Headers等
- 一定要设置decodeSlash为false才不会进行解码;
- 因为用了不同的“契约”,需要新写一个类使用@RequestLine,并进行配置