WebClient uri의 path variable 이슈
시작
WebClient를 활용하여 Github 특정 repository의 특정 경로에 README.md 파일을 생성하기 위해 Github API를 호출하려는 데 다음과 같은 에러가 발생하였고 이를 해결하고자 했다.
과정
다음 코드를 실행했을 때 문제가 발생하였다.
RequestBodySpec spec = githubApiClient.put()
.uri("/repos/{userName}/{repoName}/contents/{dirPath}/README.md",
userName, repoName, dirPath);
breakpoint를 통해 확인해보니 dirPath의 값이 length가 0인 empty string 일때 발생했다(이미지에서 contents//README.md
를 확인할 수 있다).
curl 명령어를 통해서도 breakpoint에서 찍혀있는 uri 그대로 API 호출을 해보았다.
curl -X PUT -H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token gho_1q2w3e4r1q2w3e4r1q2w3e4r" \
"https://api.github.com/repos/hoonti06/test10/contents//README.md" \
-d '{"message":"commit message", "content":"Y29udGVudHM="}'
# 참고 : content는 base64 형식이어야 한다.
동일하게 다음과 같은 에러를 응답 받을 수 있었다.
{
"message": "path cannot start with a slash",
"errors": [
{
"resource": "Commit",
"field": "path",
"code": "invalid"
}
],
"documentation_url": "https://docs.github.com/rest/reference/repos#create-or-update-file-contents"
}
위 코드를 debugging 해봤더니 uri가 완성되기 전에 연속된 slash(//)를 하나의 slash로 변환해주는 sanitize를 수행하게 되어 결국 완성된 uri에 있는 연속 slash는 제거가 안 됐던 것이다.
그래서 String.format을 통해 미리 uri를 완성해놓고 그 값을 첫 번째 parameter로 전달했다.
// dirPath가 root("")일 경우를 위해 String.format() 사용
String uri = String
.format("/repos/%s/%s/contents/%s/README.md", user.getName(), repoName, dirPath);
RequestBodySpec spec = githubApiClient.put()
.uri(uri);
위 코드를 debugging 해보니 sanitize를 통해 slash가 하나로 변환되었다.
그래서 String.format을 적용하여 해결되는 듯 했으나 또 다른 문제가 발생했다. 이번 문제는 dirPath에 띄어쓰기나 !,@ 등의 특수문자가 포함되어 있을 때 발생했다.
String.format()의 경우 특수문자를 encoding해주지 않지만, uri() 메서드의 parameter로 전달되는 값들은 encoding을 해주기 때문에
첫 번째 코드가 제대로 동작한다.
RequestBodySpec spec = githubApiClient.put()
.uri("/repos/{userName}/{repoName}/contents/{dirPath}/README.md",
userName, repoName, dirPath);
그래서 오버로딩되어 있는 다른 uri 메서드를 활용하였다. Map 변수를 parameter로 전달할 수 있는데, 이때, dirPath가 empty string일 경우 Map에 insert하지 않도록 하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
StringBuilder uri = new StringBuilder("/repos/{userName}/{repoName}/contents");
Map<String, String> uriPathVariables = new HashMap<>();
uriPathVariables.put("userName", user.getName());
uriPathVariables.put("repoName", repoName);
if (dirPath != null && !dirPath.equals("")) {
uri.append("/{dirPath}");
uriPathVariables.put("dirPath", dirPath);
}
uri.append("/README.md");
RequestBodySpec spec = githubApiClient.put()
.uri(uri.toString(), uriPathVariables);
마무리
- 여러 번 수정하는 일이 없도록 처음부터 좀 더 많은 경우를 고려하여 코드를 작성해야겠다.
- 오버로딩되어 있는 메서드를 적절히 사용해보았던 경험이 되었다.
- 단순 library 사용에 그치는게 아닌, 직접 dubugging을 통해 코드가 어떻게 진행되는지 확인해 본 좋은 경험이었다.