Swagger Codegen CLI Java 客户端 - 如何正确使用它
Swagger Codegen CLI Java Client - How to use it right
我目前正在使用我的 jersey2 休息服务。为了更好地了解给定服务(描述、类型等),我大量使用了 swagger (swagger-jersey2-jaxrs)。
因此,我能够生成我的服务描述 (swagger.json),并且我可以通过 swagger ui.
查看和探索它们
现在我需要创建一些客户端来使用这些服务。我遇到了 swagger codegen cli,这是一个很好的工具来生成你的客户端和许多不同的语言(java 在我的例子中)。我能够生成 api 客户端和正在使用的模型。
这里我遇到了第一个问题。 REST 服务和 swagger 描述受 http 基本身份验证保护。我阅读了 documentation,它给了我一些提示,表明有可能使用基本身份验证。在这一点上,我不得不提到,从我的角度来看,文档非常糟糕。它说:
-a , --auth
在远程获取 swagger 定义时添加授权 headers。传入 name:header 的 URL-encoded 字符串,用逗号分隔多个值。
我首先想到的是像在 http header 中传递一个字符串,但这不起作用,甚至谷歌搜索如何使用带有 swagger cli 的基本身份验证也没有得到一些明确的答案。经过大量的尝试和错误我(我使用的是 CLI 2.1.2)我终于找到了正确的格式:
java -jar swagger-codegen-cli-2.1.2.jar 生成 -a "Authorization: Basic YWRtaW46YWRtaW4=" -i http://localhost:8080/webproject/restapi/swagger.json -l java -o restclient
在我的例子中,YWRtaW46YWRtaW4= 是 admin:admin 的 base64 编码值。
到目前为止一切顺利。生成的 java 客户端也必须使用基本身份验证。我查看了 ApiClient 的方法并找到了 setUsername 和 setPassword。我认为这种方法使客户端能够使用基本身份验证但没有运气。
所以我深入研究了生成的类,尤其是ApiClient和几个生成的ApiService类。
我发现 setUsername 和 setPassword 没有效果,原因如下:
/**
* Helper method to set username for the first HTTP basic authentication.
*/
public void setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
同时HashMap定义如下:
// Setup authentications (key: authentication name, value: authentication).
authentications = new HashMap<String, Authentication>();
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
身份验证哈希图变得不可变,但为什么呢?目的是什么?此外,ApiClinet 中没有生成所需 auth object 的辅助方法,因此我执行了以下操作:
1) 注释掉 authentications Collections.unmodifiableMap(authentications) 行,这样 hashmap 就可以再次修改了
2) 手动创建需要的授权object
HttpBasicAuth authentication = new HttpBasicAuth();
authentication.setUsername("admin");
authentication.setPassword("admin");
3) 添加 auth object 到 apiClients authentication hashmap:
ApiClient apiClient = new ApiClient();
apiClient.setBasePath(basePath);
apiClient.getAuthentications().put("basic", authentication);
4) 修改invokeApi方法(ApiClient.java)
public String invokeAPI(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, Map<String, String> formParams, String accept, String contentType, String[] authNames) throws ApiException {
String authNames2[] = {"basic"};
updateParamsForAuth(authNames2, queryParams, headerParams);
//updateParamsForAuth(authNames, queryParams, headerParams);
...
第 4 步是必要的,因为 ApiServices 调用 apiClient 方法如下:
String[] authNames = new String[] { };
String response = apiClient.invokeAPI(path, "POST", queryParams, postBody, headerParams, formParams, accept, contentType, authNames);
另一种可能的解决方案是在每个 api 服务中定义身份验证哈希映射的密钥,例如:
String[] authNames = new String[] { "basic" };
完成所有修改后,一切都按预期工作,但我不认为这是自动生成的休息客户端背后的想法。
所以我的问题是:我是否遗漏了一些要点,或者我应该将 swagger 生成的客户端(在本例中为 java)更多地视为正在开发的测试版解决方案?
请理解我的意思,我认为整个 swagger 框架(jersey2 支持、openapi、swaggerui、codegen)是一件很棒的事情,我感谢开发人员的努力,但我想正确使用 codegen我不认为背后的想法是必须以这种方式自定义生成的 ApiClient 和 ApiServices。
问题是您的规范没有提到您要使用的安全类型(a.k.a。安全定义)或哪个安全定义适用于哪个端点。
swagger 规范是 here,但它并不能说明全部。
您需要做的是 1. 设置安全定义。这是一个简单的基本 http 身份验证定义:
securityDefinitions:
basic:
type: basic
description: HTTP Basic Authentication.
和 2. 在终点使用该安全定义。
paths:
/:
get:
security:
- basic: []
responses:
200:
description: OK
然后重新生成您的 swagger 客户端代码。它应该正确设置不可变映射和 authNames 数组。
如前所述,如果您不想修改现有代码,可以在自定义配置中扩展 ApiClient
,例如
@Configuration
public class Config {
@Value("${baseUrl}")
private String baseUrl;
protected class AuthApiClient extends ApiClient {
public AuthApiClient() {
super();
}
@Override
public <T> T invokeAPI(final String path, final HttpMethod method,
final MultiValueMap<String, String> queryParams, final Object body,
final HttpHeaders headerParams, final MultiValueMap<String, Object> formParams,
final List<MediaType> accept, final MediaType contentType,
final String[] authNames, final ParameterizedTypeReference<T> returnType)
throws RestClientException {
final HttpBasicAuth auth = new HttpBasicAuth();
auth.setUsername("myUsername");
auth.setPassword("myPassword");
auth.applyToParams(queryParams, headerParams);
return super.invokeAPI(path, method, queryParams, body, headerParams, formParams,
accept, contentType, authNames, returnType);
}
}
@Bean
@Primary
@Qualifier("MyApiClient")
public AuthApiClient myApiClient() {
final AuthApiClient apiClient = new AuthApiClient();
apiClient.setBasePath(this.baseUrl);
return apiClient;
}
}
我目前正在使用我的 jersey2 休息服务。为了更好地了解给定服务(描述、类型等),我大量使用了 swagger (swagger-jersey2-jaxrs)。 因此,我能够生成我的服务描述 (swagger.json),并且我可以通过 swagger ui.
查看和探索它们现在我需要创建一些客户端来使用这些服务。我遇到了 swagger codegen cli,这是一个很好的工具来生成你的客户端和许多不同的语言(java 在我的例子中)。我能够生成 api 客户端和正在使用的模型。
这里我遇到了第一个问题。 REST 服务和 swagger 描述受 http 基本身份验证保护。我阅读了 documentation,它给了我一些提示,表明有可能使用基本身份验证。在这一点上,我不得不提到,从我的角度来看,文档非常糟糕。它说:
-a , --auth 在远程获取 swagger 定义时添加授权 headers。传入 name:header 的 URL-encoded 字符串,用逗号分隔多个值。
我首先想到的是像在 http header 中传递一个字符串,但这不起作用,甚至谷歌搜索如何使用带有 swagger cli 的基本身份验证也没有得到一些明确的答案。经过大量的尝试和错误我(我使用的是 CLI 2.1.2)我终于找到了正确的格式:
java -jar swagger-codegen-cli-2.1.2.jar 生成 -a "Authorization: Basic YWRtaW46YWRtaW4=" -i http://localhost:8080/webproject/restapi/swagger.json -l java -o restclient
在我的例子中,YWRtaW46YWRtaW4= 是 admin:admin 的 base64 编码值。
到目前为止一切顺利。生成的 java 客户端也必须使用基本身份验证。我查看了 ApiClient 的方法并找到了 setUsername 和 setPassword。我认为这种方法使客户端能够使用基本身份验证但没有运气。
所以我深入研究了生成的类,尤其是ApiClient和几个生成的ApiService类。 我发现 setUsername 和 setPassword 没有效果,原因如下:
/**
* Helper method to set username for the first HTTP basic authentication.
*/
public void setUsername(String username) {
for (Authentication auth : authentications.values()) {
if (auth instanceof HttpBasicAuth) {
((HttpBasicAuth) auth).setUsername(username);
return;
}
}
throw new RuntimeException("No HTTP basic authentication configured!");
}
同时HashMap定义如下:
// Setup authentications (key: authentication name, value: authentication).
authentications = new HashMap<String, Authentication>();
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
身份验证哈希图变得不可变,但为什么呢?目的是什么?此外,ApiClinet 中没有生成所需 auth object 的辅助方法,因此我执行了以下操作:
1) 注释掉 authentications Collections.unmodifiableMap(authentications) 行,这样 hashmap 就可以再次修改了
2) 手动创建需要的授权object
HttpBasicAuth authentication = new HttpBasicAuth();
authentication.setUsername("admin");
authentication.setPassword("admin");
3) 添加 auth object 到 apiClients authentication hashmap:
ApiClient apiClient = new ApiClient();
apiClient.setBasePath(basePath);
apiClient.getAuthentications().put("basic", authentication);
4) 修改invokeApi方法(ApiClient.java)
public String invokeAPI(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, Map<String, String> formParams, String accept, String contentType, String[] authNames) throws ApiException {
String authNames2[] = {"basic"};
updateParamsForAuth(authNames2, queryParams, headerParams);
//updateParamsForAuth(authNames, queryParams, headerParams);
...
第 4 步是必要的,因为 ApiServices 调用 apiClient 方法如下:
String[] authNames = new String[] { };
String response = apiClient.invokeAPI(path, "POST", queryParams, postBody, headerParams, formParams, accept, contentType, authNames);
另一种可能的解决方案是在每个 api 服务中定义身份验证哈希映射的密钥,例如:
String[] authNames = new String[] { "basic" };
完成所有修改后,一切都按预期工作,但我不认为这是自动生成的休息客户端背后的想法。 所以我的问题是:我是否遗漏了一些要点,或者我应该将 swagger 生成的客户端(在本例中为 java)更多地视为正在开发的测试版解决方案? 请理解我的意思,我认为整个 swagger 框架(jersey2 支持、openapi、swaggerui、codegen)是一件很棒的事情,我感谢开发人员的努力,但我想正确使用 codegen我不认为背后的想法是必须以这种方式自定义生成的 ApiClient 和 ApiServices。
问题是您的规范没有提到您要使用的安全类型(a.k.a。安全定义)或哪个安全定义适用于哪个端点。
swagger 规范是 here,但它并不能说明全部。
您需要做的是 1. 设置安全定义。这是一个简单的基本 http 身份验证定义:
securityDefinitions:
basic:
type: basic
description: HTTP Basic Authentication.
和 2. 在终点使用该安全定义。
paths:
/:
get:
security:
- basic: []
responses:
200:
description: OK
然后重新生成您的 swagger 客户端代码。它应该正确设置不可变映射和 authNames 数组。
如前所述,如果您不想修改现有代码,可以在自定义配置中扩展 ApiClient
,例如
@Configuration
public class Config {
@Value("${baseUrl}")
private String baseUrl;
protected class AuthApiClient extends ApiClient {
public AuthApiClient() {
super();
}
@Override
public <T> T invokeAPI(final String path, final HttpMethod method,
final MultiValueMap<String, String> queryParams, final Object body,
final HttpHeaders headerParams, final MultiValueMap<String, Object> formParams,
final List<MediaType> accept, final MediaType contentType,
final String[] authNames, final ParameterizedTypeReference<T> returnType)
throws RestClientException {
final HttpBasicAuth auth = new HttpBasicAuth();
auth.setUsername("myUsername");
auth.setPassword("myPassword");
auth.applyToParams(queryParams, headerParams);
return super.invokeAPI(path, method, queryParams, body, headerParams, formParams,
accept, contentType, authNames, returnType);
}
}
@Bean
@Primary
@Qualifier("MyApiClient")
public AuthApiClient myApiClient() {
final AuthApiClient apiClient = new AuthApiClient();
apiClient.setBasePath(this.baseUrl);
return apiClient;
}
}