如何使用 springframework/spring-boot 的 RestTemplate 在 Firebase Remote Config REST Api 的实现中获取 "etag"?
How to get "etag" in the implementation of Firebase Remote Config REST Api using the RestTemplate of springframework/spring-boot?
我正在 spring-boot 服务器中实现 firebase-remote-config rest api 用于 Android 应用程序以供学习。我的 spring 引导版本是 2.1.7.RELEASE。我正在尝试获取 etag 值,以便我可以从服务器端更新键和值,而无需使用 firebase 控制台。根据此 link,我能够通过 httpconnection 获得 etag。但是每当我使用 RestTemplate 时,我都无法获得 etag,我得到的只是 null.
这是build.gradle文件
buildscript {
ext {
springBootVersion = '2.1.7.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.practice.shaikhalvee'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
compile group: 'com.google.apis', name: 'google-api-services-firebaseremoteconfig', version: 'v1-rev14-1.23.0'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.5'
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
这是我创建的用于试验 firebase-remote-config 的控制器。
@RestController
@RequestMapping(value = "/api/remote-config")
public class FirebaseRestApiEndpoint {
private final FirebaseRemoteConfigService firebaseRemoteConfigService;
public FirebaseRestApiEndpoint(FirebaseRemoteConfigService firebaseRemoteConfigService) {
this.firebaseRemoteConfigService = firebaseRemoteConfigService;
}
@GetMapping(value = "/get-template/{call-type}")
public void getTemplate(@PathVariable("call-type") String callType) {
switch (callType) {
case "http":
firebaseRemoteConfigService.getMetadataTemplateWithHttpCall();
break;
case "rest":
firebaseRemoteConfigService.getMetadataTemplateWithRestCall();
break;
default:
throw new RuntimeException("call-type is unknown. Should be rest or http");
}
}
}
这是我使用的accessToken()过程
// Constant File holds the url and other static final string values.
public static String getAccessToken() throws IOException {
GoogleCredential googleCredential = GoogleCredential
.fromStream(new FileInputStream(Constants.CERTIFICATE_FILE))
.createScoped(Arrays.asList(Constants.SCOPES));
googleCredential.refreshToken();
return googleCredential.getAccessToken();
}
对于服务层,为了简化,我只包括此处调用的方法。
这是使用 HttpUrlConnection 的方法。
public void getMetadataTemplateWithHttpCall() {
try {
URL url = new URL(Constants.BASE_URL + Constants.REMOTE_CONFIG_ENDPOINT);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestProperty("Authorization", "Bearer " + CommonConfig.getAccessToken());
httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setRequestProperty("Accept-Encoding", "gzip");
int code = httpURLConnection.getResponseCode();
if (code == 200) {
InputStream inputStream = new GZIPInputStream(httpURLConnection.getInputStream());
JsonElement jsonElement = JsonParser.parseReader(new InputStreamReader(inputStream));
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.serializeSpecialFloatingPointValues()
.create();
String jsonStr = gson.toJson(jsonElement);
RemoteConfig remoteConfig = gson.fromJson(jsonStr, RemoteConfig.class);
String etag = httpURLConnection.getHeaderField("ETag");
System.out.println(etag);
}
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
此处根据 Firebase-Remote-Config REST Api.
的 documentation 打印正确的 etag
但是如果我尝试使用 RestTemplate 实现相同的 http get 连接,我无法获得 etag。
现在就是利用RestTemplate的方法了。对于这次休息电话,我提出了非常相似的请求 属性。但我仍然无法获得 etag
public void getMetadataTemplateWithRestCall() {
ObjectMapper objectMapper = new ObjectMapper();
HttpHeaders httpHeaders = new HttpHeaders();
String url = "";
try {
httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
httpHeaders.setBearerAuth(CommonConfig.getAccessToken());
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
url = Constants.BASE_URL + Constants.REMOTE_CONFIG_ENDPOINT;
HttpEntity<?> requestEntity = new HttpEntity(httpHeaders);
ResponseEntity<RemoteConfig> baseResponse = restTemplate.exchange(url, HttpMethod.GET, requestEntity, RemoteConfig.class);
String etag = baseResponse.getHeaders().getETag();
System.out.println(etag);
} catch (IOException e) {
e.getMessage();
}
}
如你所料,这个etag值为null。我正在尝试 Firebase Remote Config REST 调用,但如果我无法正确处理 REST 调用,可能是我遗漏了什么。
为了您的方便,我提供了他们的 header,因为 etag 位于 header。 有趣的是,在 http 调用中,我得到 13 headers。在休息电话中我得到 11。你猜对了,etag 丢失了
这是 http 连接调用的 header。
Transfer-Encoding=[chunked],
null=[HTTP/1.1 200 OK],
Alt-Svc=[quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000],
Server=[ESF],
X-Content-Type-Options=[nosniff],
Date=[Mon, 11 Nov 2019 04:59:04 GMT],
X-Frame-Options=[SAMEORIGIN],
Cache-Control=[private],
ETag=[etag-111111311162-47],
Content-Encoding=[gzip],
Vary=[Referer, X-Origin, Origin],
X-XSS-Protection=[0],
Content-Type=[application/json; charset=UTF-8]
这是休息电话header
Content-Type:"application/json; charset=UTF-8",
Vary:"X-Origin", "Referer", "Origin,Accept-Encoding",
Date:"Mon, 11 Nov 2019 05:15:10 GMT",
Server:"ESF",
Cache-Control:"private",
X-XSS-Protection:"0",
X-Frame-Options:"SAMEORIGIN",
X-Content-Type-Options:"nosniff",
Alt-Svc:"quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000",
Accept-Ranges:"none",
Transfer-Encoding:"chunked"
谁能帮帮我。我的目标是使用 rest 调用获取 etag 值,更准确地说是使用 springframework 的 RestTemplate,因为它是一个 REST API.
谢谢。
经过长时间的研究和调试,我找到了解决方法。
在 documentation 中,我们被告知添加 header Accept-Encoding: gzip。这就是我在 getMetadataTemplateWithHttpCall()
方法中使用 httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
的原因。它不起作用。我什至这样添加了header,httpHeaders.add("Accept-Encoding", "gzip");
。它抛出异常,RestTemplate 不接受 gzip 作为 Accept-Encoding。有效的是,如果您在调用之前使用自定义的 RestTemplate 拦截器拦截其余调用,并使用带有拦截器的 gzip 压缩请求。这意味着在 RestTemplate 的情况下,我们需要在 http 调用之前压缩请求然后发送它。只有这样,在 ResponseEntity 我得到 "etag".
我已经为指定的 RestTemplate 创建了一个 @Bean
,我在下面提供了它。如果你想使用springframework的RestTemplate,那么你必须添加这种类型的拦截器才能使用Firebase Remote Config REST API.
这是@Bean
@Configuration
public class RestTemplateConfig {
@Bean(value = "gzippedRestTemplate")
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
HttpClientBuilder.create().build());
clientHttpRequestFactory.setConnectTimeout(2000);
clientHttpRequestFactory.setReadTimeout(10000);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors == null) {
interceptors = new ArrayList<>();
restTemplate.setInterceptors(interceptors);
}
interceptors.add(new GzipAcceptHeaderRequestInterceptor());
return restTemplate;
}
public static class GzipAcceptHeaderRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, @Nullable byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set(HttpHeaders.ACCEPT_ENCODING, "gzip");
return execution.execute(request, body);
}
}
}
现在您可以使用 springframework 的 RestTemplate 从 GET
调用中获取 "etag" 值,发布您的作品。
我正在 spring-boot 服务器中实现 firebase-remote-config rest api 用于 Android 应用程序以供学习。我的 spring 引导版本是 2.1.7.RELEASE。我正在尝试获取 etag 值,以便我可以从服务器端更新键和值,而无需使用 firebase 控制台。根据此 link,我能够通过 httpconnection 获得 etag。但是每当我使用 RestTemplate 时,我都无法获得 etag,我得到的只是 null.
这是build.gradle文件
buildscript {
ext {
springBootVersion = '2.1.7.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.practice.shaikhalvee'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
compile group: 'com.google.apis', name: 'google-api-services-firebaseremoteconfig', version: 'v1-rev14-1.23.0'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.5'
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
这是我创建的用于试验 firebase-remote-config 的控制器。
@RestController
@RequestMapping(value = "/api/remote-config")
public class FirebaseRestApiEndpoint {
private final FirebaseRemoteConfigService firebaseRemoteConfigService;
public FirebaseRestApiEndpoint(FirebaseRemoteConfigService firebaseRemoteConfigService) {
this.firebaseRemoteConfigService = firebaseRemoteConfigService;
}
@GetMapping(value = "/get-template/{call-type}")
public void getTemplate(@PathVariable("call-type") String callType) {
switch (callType) {
case "http":
firebaseRemoteConfigService.getMetadataTemplateWithHttpCall();
break;
case "rest":
firebaseRemoteConfigService.getMetadataTemplateWithRestCall();
break;
default:
throw new RuntimeException("call-type is unknown. Should be rest or http");
}
}
}
这是我使用的accessToken()过程
// Constant File holds the url and other static final string values.
public static String getAccessToken() throws IOException {
GoogleCredential googleCredential = GoogleCredential
.fromStream(new FileInputStream(Constants.CERTIFICATE_FILE))
.createScoped(Arrays.asList(Constants.SCOPES));
googleCredential.refreshToken();
return googleCredential.getAccessToken();
}
对于服务层,为了简化,我只包括此处调用的方法。
这是使用 HttpUrlConnection 的方法。
public void getMetadataTemplateWithHttpCall() {
try {
URL url = new URL(Constants.BASE_URL + Constants.REMOTE_CONFIG_ENDPOINT);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestProperty("Authorization", "Bearer " + CommonConfig.getAccessToken());
httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setRequestProperty("Accept-Encoding", "gzip");
int code = httpURLConnection.getResponseCode();
if (code == 200) {
InputStream inputStream = new GZIPInputStream(httpURLConnection.getInputStream());
JsonElement jsonElement = JsonParser.parseReader(new InputStreamReader(inputStream));
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.serializeSpecialFloatingPointValues()
.create();
String jsonStr = gson.toJson(jsonElement);
RemoteConfig remoteConfig = gson.fromJson(jsonStr, RemoteConfig.class);
String etag = httpURLConnection.getHeaderField("ETag");
System.out.println(etag);
}
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
此处根据 Firebase-Remote-Config REST Api.
的 documentation 打印正确的 etag但是如果我尝试使用 RestTemplate 实现相同的 http get 连接,我无法获得 etag。
现在就是利用RestTemplate的方法了。对于这次休息电话,我提出了非常相似的请求 属性。但我仍然无法获得 etag
public void getMetadataTemplateWithRestCall() {
ObjectMapper objectMapper = new ObjectMapper();
HttpHeaders httpHeaders = new HttpHeaders();
String url = "";
try {
httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
httpHeaders.setBearerAuth(CommonConfig.getAccessToken());
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
url = Constants.BASE_URL + Constants.REMOTE_CONFIG_ENDPOINT;
HttpEntity<?> requestEntity = new HttpEntity(httpHeaders);
ResponseEntity<RemoteConfig> baseResponse = restTemplate.exchange(url, HttpMethod.GET, requestEntity, RemoteConfig.class);
String etag = baseResponse.getHeaders().getETag();
System.out.println(etag);
} catch (IOException e) {
e.getMessage();
}
}
如你所料,这个etag值为null。我正在尝试 Firebase Remote Config REST 调用,但如果我无法正确处理 REST 调用,可能是我遗漏了什么。
为了您的方便,我提供了他们的 header,因为 etag 位于 header。 有趣的是,在 http 调用中,我得到 13 headers。在休息电话中我得到 11。你猜对了,etag 丢失了
这是 http 连接调用的 header。
Transfer-Encoding=[chunked],
null=[HTTP/1.1 200 OK],
Alt-Svc=[quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000],
Server=[ESF],
X-Content-Type-Options=[nosniff],
Date=[Mon, 11 Nov 2019 04:59:04 GMT],
X-Frame-Options=[SAMEORIGIN],
Cache-Control=[private],
ETag=[etag-111111311162-47],
Content-Encoding=[gzip],
Vary=[Referer, X-Origin, Origin],
X-XSS-Protection=[0],
Content-Type=[application/json; charset=UTF-8]
这是休息电话header
Content-Type:"application/json; charset=UTF-8",
Vary:"X-Origin", "Referer", "Origin,Accept-Encoding",
Date:"Mon, 11 Nov 2019 05:15:10 GMT",
Server:"ESF",
Cache-Control:"private",
X-XSS-Protection:"0",
X-Frame-Options:"SAMEORIGIN",
X-Content-Type-Options:"nosniff",
Alt-Svc:"quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000",
Accept-Ranges:"none",
Transfer-Encoding:"chunked"
谁能帮帮我。我的目标是使用 rest 调用获取 etag 值,更准确地说是使用 springframework 的 RestTemplate,因为它是一个 REST API.
谢谢。
经过长时间的研究和调试,我找到了解决方法。
在 documentation 中,我们被告知添加 header Accept-Encoding: gzip。这就是我在 getMetadataTemplateWithHttpCall()
方法中使用 httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
的原因。它不起作用。我什至这样添加了header,httpHeaders.add("Accept-Encoding", "gzip");
。它抛出异常,RestTemplate 不接受 gzip 作为 Accept-Encoding。有效的是,如果您在调用之前使用自定义的 RestTemplate 拦截器拦截其余调用,并使用带有拦截器的 gzip 压缩请求。这意味着在 RestTemplate 的情况下,我们需要在 http 调用之前压缩请求然后发送它。只有这样,在 ResponseEntity 我得到 "etag".
我已经为指定的 RestTemplate 创建了一个 @Bean
,我在下面提供了它。如果你想使用springframework的RestTemplate,那么你必须添加这种类型的拦截器才能使用Firebase Remote Config REST API.
这是@Bean
@Configuration
public class RestTemplateConfig {
@Bean(value = "gzippedRestTemplate")
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
HttpClientBuilder.create().build());
clientHttpRequestFactory.setConnectTimeout(2000);
clientHttpRequestFactory.setReadTimeout(10000);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (interceptors == null) {
interceptors = new ArrayList<>();
restTemplate.setInterceptors(interceptors);
}
interceptors.add(new GzipAcceptHeaderRequestInterceptor());
return restTemplate;
}
public static class GzipAcceptHeaderRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, @Nullable byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set(HttpHeaders.ACCEPT_ENCODING, "gzip");
return execution.execute(request, body);
}
}
}
现在您可以使用 springframework 的 RestTemplate 从 GET
调用中获取 "etag" 值,发布您的作品。