Skip to content

[Java][okhttp-gson] Making API response headers case insensitive #3029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/generators/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ sidebar_label: java
|useRuntimeException|Use RuntimeException instead of Exception| |false|
|feignVersion|Version of OpenFeign: '10.x', '9.x' (default)| |false|
|useReflectionEqualsHashCode|Use org.apache.commons.lang3.builder for equals and hashCode in the models. WARNING: This will fail under a security manager, unless the appropriate permissions are set up correctly and also there's potential performance impact.| |false|
|caseInsensitiveResponseHeaders|Make API response's headers case-insensitive in okhttp-gson library| |false|
|library|library template (sub-template) to use|<dl><dt>**jersey1**</dt><dd>HTTP client: Jersey client 1.19.x. JSON processing: Jackson 2.8.x. Enable Java6 support using '-DsupportJava6=true'. Enable gzip request encoding using '-DuseGzipFeature=true'. IMPORTANT NOTE: jersey 1.x is no longer actively maintained so please upgrade to 'jersey2' or other HTTP libaries instead.</dd><dt>**jersey2**</dt><dd>HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.8.x</dd><dt>**feign**</dt><dd>HTTP client: OpenFeign 9.x or 10.x. JSON processing: Jackson 2.8.x. To enable OpenFeign 10.x, set the 'feignVersion' option to '10.x'</dd><dt>**okhttp-gson**</dt><dd>[DEFAULT] HTTP client: OkHttp 3.x. JSON processing: Gson 2.8.x. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.</dd><dt>**retrofit**</dt><dd>HTTP client: OkHttp 2.x. JSON processing: Gson 2.x (Retrofit 1.9.0). IMPORTANT NOTE: retrofit1.x is no longer actively maintained so please upgrade to 'retrofit2' instead.</dd><dt>**retrofit2**</dt><dd>HTTP client: OkHttp 3.x. JSON processing: Gson 2.x (Retrofit 2.3.0). Enable the RxJava adapter using '-DuseRxJava[2]=true'. (RxJava 1.x or 2.x)</dd><dt>**resttemplate**</dt><dd>HTTP client: Spring RestTemplate 4.x. JSON processing: Jackson 2.8.x</dd><dt>**webclient**</dt><dd>HTTP client: Spring WebClient 5.x. JSON processing: Jackson 2.9.x</dd><dt>**resteasy**</dt><dd>HTTP client: Resteasy client 3.x. JSON processing: Jackson 2.8.x</dd><dt>**vertx**</dt><dd>HTTP client: VertX client 3.x. JSON processing: Jackson 2.8.x</dd><dt>**google-api-client**</dt><dd>HTTP client: Google API client 1.x. JSON processing: Jackson 2.8.x</dd><dt>**rest-assured**</dt><dd>HTTP client: rest-assured : 3.x. JSON processing: Gson 2.x. Only for Java8</dd><dl>|okhttp-gson|
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
public static final String PARCELABLE_MODEL = "parcelableModel";
public static final String USE_RUNTIME_EXCEPTION = "useRuntimeException";
public static final String USE_REFLECTION_EQUALS_HASHCODE = "useReflectionEqualsHashCode";
public static final String CASE_INSENSITIVE_RESPONSE_HEADERS = "caseInsensitiveResponseHeaders";

public static final String PLAY_24 = "play24";
public static final String PLAY_25 = "play25";
Expand Down Expand Up @@ -90,6 +91,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
protected boolean useGzipFeature = false;
protected boolean useRuntimeException = false;
protected boolean useReflectionEqualsHashCode = false;
protected boolean caseInsensitiveResponseHeaders = false;
protected String authFolder;

public JavaClientCodegen() {
Expand Down Expand Up @@ -122,6 +124,8 @@ public JavaClientCodegen() {
cliOptions.add(CliOption.newBoolean(USE_RUNTIME_EXCEPTION, "Use RuntimeException instead of Exception"));
cliOptions.add(CliOption.newBoolean(FEIGN_VERSION, "Version of OpenFeign: '10.x', '9.x' (default)"));
cliOptions.add(CliOption.newBoolean(USE_REFLECTION_EQUALS_HASHCODE, "Use org.apache.commons.lang3.builder for equals and hashCode in the models. WARNING: This will fail under a security manager, unless the appropriate permissions are set up correctly and also there's potential performance impact."));
cliOptions.add(CliOption.newBoolean(CASE_INSENSITIVE_RESPONSE_HEADERS, "Make API response's headers case-insensitive in " + OKHTTP_GSON + " library"));


supportedLibraries.put(JERSEY1, "HTTP client: Jersey client 1.19.x. JSON processing: Jackson 2.8.x. Enable Java6 support using '-DsupportJava6=true'. Enable gzip request encoding using '-DuseGzipFeature=true'. IMPORTANT NOTE: jersey 1.x is no longer actively maintained so please upgrade to 'jersey2' or other HTTP libaries instead.");
supportedLibraries.put(JERSEY2, "HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.8.x");
Expand Down Expand Up @@ -232,6 +236,10 @@ public void processOpts() {
this.setUseReflectionEqualsHashCode(convertPropertyToBooleanAndWriteBack(USE_REFLECTION_EQUALS_HASHCODE));
}

if (additionalProperties.containsKey(CASE_INSENSITIVE_RESPONSE_HEADERS)) {
this.setUseReflectionEqualsHashCode(convertPropertyToBooleanAndWriteBack(CASE_INSENSITIVE_RESPONSE_HEADERS));
}

final String invokerFolder = (sourceFolder + '/' + invokerPackage).replace(".", "/");
final String apiFolder = (sourceFolder + '/' + apiPackage).replace(".", "/");
authFolder = (sourceFolder + '/' + invokerPackage + ".auth").replace(".", "/");
Expand Down Expand Up @@ -659,6 +667,10 @@ public void setUseReflectionEqualsHashCode(boolean useReflectionEqualsHashCode)
this.useReflectionEqualsHashCode = useReflectionEqualsHashCode;
}

public void setCaseInsensitiveResponseHeaders(final Boolean caseInsensitiveResponseHeaders) {
this.caseInsensitiveResponseHeaders = caseInsensitiveResponseHeaders;
}

final private static Pattern JSON_MIME_PATTERN = Pattern.compile("(?i)application\\/json(;.*)?");
final private static Pattern JSON_VENDOR_MIME_PATTERN = Pattern.compile("(?i)application\\/vnd.(.*)+json(;.*)?");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ package {{invokerPackage}};

import java.util.List;
import java.util.Map;
{{#caseInsensitiveResponseHeaders}}
import java.util.Map.Entry;
import java.util.TreeMap;
{{/caseInsensitiveResponseHeaders}}

/**
* API response returned by API call.
Expand All @@ -30,7 +34,13 @@ public class ApiResponse<T> {
*/
public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) {
this.statusCode = statusCode;
this.headers = headers;
{{#caseInsensitiveResponseHeaders}}
Map<String, List<String>> responseHeaders = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
for(Entry<String, List<String>> entry : headers.entrySet()){
responseHeaders.put(entry.getKey().toLowerCase(), entry.getValue());
}
{{/caseInsensitiveResponseHeaders}}
this.headers = {{#caseInsensitiveResponseHeaders}}responseHeaders{{/caseInsensitiveResponseHeaders}}{{^caseInsensitiveResponseHeaders}}headers{{/caseInsensitiveResponseHeaders}};
this.data = data;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{{>licenseInfo}}

package {{invokerPackage}};

import java.util.Map;
import java.util.List;
{{#caseInsensitiveResponseHeaders}}
import java.util.Map.Entry;
import java.util.TreeMap;
{{/caseInsensitiveResponseHeaders}}

{{>generatedAnnotation}}
public class ApiException extends{{#useRuntimeException}} RuntimeException {{/useRuntimeException}}{{^useRuntimeException}} Exception {{/useRuntimeException}}{
private int code = 0;
private Map<String, List<String>> responseHeaders = null;
private String responseBody = null;

public ApiException() {}

public ApiException(Throwable throwable) {
super(throwable);
}

public ApiException(String message) {
super(message);
}

public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders, String responseBody) {
super(message, throwable);
this.code = code;
{{#caseInsensitiveResponseHeaders}}
Map<String, List<String>> headers = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
for(Entry<String, List<String>> entry : responseHeaders.entrySet()){
headers.put(entry.getKey().toLowerCase(), entry.getValue());
}
{{/caseInsensitiveResponseHeaders}}
this.responseHeaders = {{#caseInsensitiveResponseHeaders}}headers{{/caseInsensitiveResponseHeaders}}{{^caseInsensitiveResponseHeaders}}responseHeaders{{/caseInsensitiveResponseHeaders}};
this.responseBody = responseBody;
}

public ApiException(String message, int code, Map<String, List<String>> responseHeaders, String responseBody) {
this(message, (Throwable) null, code, responseHeaders, responseBody);
}

public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders) {
this(message, throwable, code, responseHeaders, null);
}

public ApiException(int code, Map<String, List<String>> responseHeaders, String responseBody) {
this((String) null, (Throwable) null, code, responseHeaders, responseBody);
}

public ApiException(int code, String message) {
super(message);
this.code = code;
}

public ApiException(int code, String message, Map<String, List<String>> responseHeaders, String responseBody) {
this(code, message);
{{#caseInsensitiveResponseHeaders}}
Map<String, List<String>> headers = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
for(Entry<String, List<String>> entry : responseHeaders.entrySet()){
headers.put(entry.getKey().toLowerCase(), entry.getValue());
}
{{/caseInsensitiveResponseHeaders}}
this.responseHeaders = {{#caseInsensitiveResponseHeaders}}headers{{/caseInsensitiveResponseHeaders}}{{^caseInsensitiveResponseHeaders}}responseHeaders{{/caseInsensitiveResponseHeaders}};
this.responseBody = responseBody;
}

/**
* Get the HTTP status code.
*
* @return HTTP status code
*/
public int getCode() {
return code;
}

/**
* Get the HTTP response headers.
*
* @return A map of list of string
*/
public Map<String, List<String>> getResponseHeaders() {
return responseHeaders;
}

/**
* Get the HTTP response body.
*
* @return Response body in the form of string
*/
public String getResponseBody() {
return responseBody;
}
}