From 40db4bae9b5f7457f9a0b51e4a013f3cc7f911d3 Mon Sep 17 00:00:00 2001 From: "Fei,Yanke" Date: Thu, 28 Apr 2022 10:48:22 +0800 Subject: [PATCH 1/5] add ResponseInterceptor support #1126 --- core/src/main/java/feign/Feign.java | 18 ++++++++-- .../main/java/feign/ResponseInterceptor.java | 29 ++++++++++++++++ .../java/feign/SynchronousMethodHandler.java | 34 +++++++++++++++---- 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 core/src/main/java/feign/ResponseInterceptor.java diff --git a/core/src/main/java/feign/Feign.java b/core/src/main/java/feign/Feign.java index 4f38a0b96..2c192606b 100644 --- a/core/src/main/java/feign/Feign.java +++ b/core/src/main/java/feign/Feign.java @@ -98,6 +98,8 @@ public static class Builder { private final List requestInterceptors = new ArrayList(); + private final List responseInterceptors = + new ArrayList(); private Logger.Level logLevel = Logger.Level.NONE; private Contract contract = new Contract.Default(); private Client client = new Client.Default(null, null); @@ -227,6 +229,14 @@ public Builder requestInterceptor(RequestInterceptor requestInterceptor) { return this; } + /** + * Adds a single response interceptor to the builder. + */ + public Builder responseInterceptor(ResponseInterceptor responseInterceptor) { + this.responseInterceptors.add(responseInterceptor); + return this; + } + /** * Sets the full set of request interceptors for the builder, overwriting any previous * interceptors. @@ -297,6 +307,9 @@ public Feign build() { List requestInterceptors = this.requestInterceptors.stream() .map(ri -> Capability.enrich(ri, capabilities)) .collect(Collectors.toList()); + List responseInterceptors = this.responseInterceptors.stream() + .map(ri -> Capability.enrich(ri, capabilities)) + .collect(Collectors.toList()); Logger logger = Capability.enrich(this.logger, capabilities); Contract contract = Capability.enrich(this.contract, capabilities); Options options = Capability.enrich(this.options, capabilities); @@ -307,8 +320,9 @@ public Feign build() { QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities); SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = - new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, - logLevel, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); + new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, + responseInterceptors,logger, logLevel, dismiss404, closeAfterDecode, + propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); diff --git a/core/src/main/java/feign/ResponseInterceptor.java b/core/src/main/java/feign/ResponseInterceptor.java new file mode 100644 index 000000000..adb20d698 --- /dev/null +++ b/core/src/main/java/feign/ResponseInterceptor.java @@ -0,0 +1,29 @@ +package feign; + +/** + * Zero or more {@code ResponseInterceptor} may be configured for purposes + * such as verify or modify headers of response, verify the business status of decoded object. + * No guarantees are given with regards to the order that interceptors are applied. + * Once interceptors are applied, {@link ResponseInterceptor#beforeDecode(Response)} is called + * before decode method called, {@link ResponseInterceptor#afterDecode(Object)} is called + * after decode method called. + */ +public interface ResponseInterceptor { + + /** + * Called for response before decode, add data on the supplied {@link Response} or doing + * customized logic + * + * @param response + * @return + */ + void beforeDecode(Response response); + + /** + * Called for response after decode, add data to decoded object or doing customized logic + * + * @param response + * @return + */ + void afterDecode(Object response); +} diff --git a/core/src/main/java/feign/SynchronousMethodHandler.java b/core/src/main/java/feign/SynchronousMethodHandler.java index 1d8f39a0d..ae89bf276 100644 --- a/core/src/main/java/feign/SynchronousMethodHandler.java +++ b/core/src/main/java/feign/SynchronousMethodHandler.java @@ -36,6 +36,7 @@ final class SynchronousMethodHandler implements MethodHandler { private final Client client; private final Retryer retryer; private final List requestInterceptors; + private final List responseInterceptors; private final Logger logger; private final Logger.Level logLevel; private final RequestTemplate.Factory buildTemplateFromArgs; @@ -48,8 +49,8 @@ final class SynchronousMethodHandler implements MethodHandler { private SynchronousMethodHandler(Target target, Client client, Retryer retryer, - List requestInterceptors, Logger logger, - Logger.Level logLevel, MethodMetadata metadata, + List requestInterceptors, List responseInterceptors, + Logger logger, Logger.Level logLevel, MethodMetadata metadata, RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder, boolean dismiss404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, @@ -60,6 +61,8 @@ private SynchronousMethodHandler(Target target, Client client, Retryer retrye this.retryer = checkNotNull(retryer, "retryer for %s", target); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors for %s", target); + this.responseInterceptors = + checkNotNull(responseInterceptors, "responseInterceptors for %s", target); this.logger = checkNotNull(logger, "logger for %s", target); this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); this.metadata = checkNotNull(metadata, "metadata for %s", target); @@ -130,9 +133,19 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + for (ResponseInterceptor interceptor: responseInterceptors) { + interceptor.beforeDecode(response); + } + + if (decoder != null) { + Object object = decoder.decode(response, metadata.returnType()); + + for (ResponseInterceptor interceptor: responseInterceptors) { + interceptor.afterDecode(object); + } - if (decoder != null) - return decoder.decode(response, metadata.returnType()); + return object; + } CompletableFuture resultFuture = new CompletableFuture<>(); asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response, @@ -143,7 +156,11 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa if (!resultFuture.isDone()) throw new IllegalStateException("Response handling not done"); - return resultFuture.join(); + Object object = resultFuture.join(); + for (ResponseInterceptor interceptor: responseInterceptors) { + interceptor.afterDecode(object); + } + return object; } catch (CompletionException e) { Throwable cause = e.getCause(); if (cause != null) @@ -179,6 +196,7 @@ static class Factory { private final Client client; private final Retryer retryer; private final List requestInterceptors; + private final List responseInterceptors; private final Logger logger; private final Logger.Level logLevel; private final boolean dismiss404; @@ -187,11 +205,13 @@ static class Factory { private final boolean forceDecoding; Factory(Client client, Retryer retryer, List requestInterceptors, + List responseInterceptors, Logger logger, Logger.Level logLevel, boolean dismiss404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) { this.client = checkNotNull(client, "client"); this.retryer = checkNotNull(retryer, "retryer"); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); + this.responseInterceptors = checkNotNull(responseInterceptors, "responseInterceptors"); this.logger = checkNotNull(logger, "logger"); this.logLevel = checkNotNull(logLevel, "logLevel"); this.dismiss404 = dismiss404; @@ -206,8 +226,8 @@ public MethodHandler create(Target target, Options options, Decoder decoder, ErrorDecoder errorDecoder) { - return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, - logLevel, md, buildTemplateFromArgs, options, decoder, + return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, + responseInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); } } From 62d8c0bcfdc8e5c97906b733b97e1a96cde156bb Mon Sep 17 00:00:00 2001 From: feiyanke Date: Mon, 2 May 2022 00:01:59 +0800 Subject: [PATCH 2/5] Add the license header. Add the license header. Co-authored-by: Dewald de Jager --- core/src/main/java/feign/ResponseInterceptor.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/feign/ResponseInterceptor.java b/core/src/main/java/feign/ResponseInterceptor.java index adb20d698..2c01b205b 100644 --- a/core/src/main/java/feign/ResponseInterceptor.java +++ b/core/src/main/java/feign/ResponseInterceptor.java @@ -1,3 +1,16 @@ +/* + * Copyright 2012-2022 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. +*/ package feign; /** From f44697d1d539806530f5b544f87a5493dac30b71 Mon Sep 17 00:00:00 2001 From: feiyanke Date: Mon, 2 May 2022 00:29:53 +0800 Subject: [PATCH 3/5] small fix for license header --- core/src/main/java/feign/ResponseInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/feign/ResponseInterceptor.java b/core/src/main/java/feign/ResponseInterceptor.java index 2c01b205b..1aa065d38 100644 --- a/core/src/main/java/feign/ResponseInterceptor.java +++ b/core/src/main/java/feign/ResponseInterceptor.java @@ -10,7 +10,7 @@ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. -*/ + */ package feign; /** From 1aeb437a07e0f364c31f420ec5222c27b780ef68 Mon Sep 17 00:00:00 2001 From: feiyanke Date: Mon, 2 May 2022 00:36:23 +0800 Subject: [PATCH 4/5] fix format issue --- core/src/main/java/feign/Feign.java | 2 +- .../main/java/feign/ResponseInterceptor.java | 41 +++++++++---------- .../java/feign/SynchronousMethodHandler.java | 12 +++--- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/feign/Feign.java b/core/src/main/java/feign/Feign.java index 2c192606b..371b7fa4c 100644 --- a/core/src/main/java/feign/Feign.java +++ b/core/src/main/java/feign/Feign.java @@ -321,7 +321,7 @@ public Feign build() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, - responseInterceptors,logger, logLevel, dismiss404, closeAfterDecode, + responseInterceptors, logger, logLevel, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, diff --git a/core/src/main/java/feign/ResponseInterceptor.java b/core/src/main/java/feign/ResponseInterceptor.java index 1aa065d38..e1057967a 100644 --- a/core/src/main/java/feign/ResponseInterceptor.java +++ b/core/src/main/java/feign/ResponseInterceptor.java @@ -14,29 +14,28 @@ package feign; /** - * Zero or more {@code ResponseInterceptor} may be configured for purposes - * such as verify or modify headers of response, verify the business status of decoded object. - * No guarantees are given with regards to the order that interceptors are applied. - * Once interceptors are applied, {@link ResponseInterceptor#beforeDecode(Response)} is called - * before decode method called, {@link ResponseInterceptor#afterDecode(Object)} is called - * after decode method called. + * Zero or more {@code ResponseInterceptor} may be configured for purposes such as verify or modify + * headers of response, verify the business status of decoded object. No guarantees are given with + * regards to the order that interceptors are applied. Once interceptors are applied, + * {@link ResponseInterceptor#beforeDecode(Response)} is called before decode method called, + * {@link ResponseInterceptor#afterDecode(Object)} is called after decode method called. */ public interface ResponseInterceptor { - /** - * Called for response before decode, add data on the supplied {@link Response} or doing - * customized logic - * - * @param response - * @return - */ - void beforeDecode(Response response); + /** + * Called for response before decode, add data on the supplied {@link Response} or doing + * customized logic + * + * @param response + * @return + */ + void beforeDecode(Response response); - /** - * Called for response after decode, add data to decoded object or doing customized logic - * - * @param response - * @return - */ - void afterDecode(Object response); + /** + * Called for response after decode, add data to decoded object or doing customized logic + * + * @param response + * @return + */ + void afterDecode(Object response); } diff --git a/core/src/main/java/feign/SynchronousMethodHandler.java b/core/src/main/java/feign/SynchronousMethodHandler.java index ae89bf276..c82ba000b 100644 --- a/core/src/main/java/feign/SynchronousMethodHandler.java +++ b/core/src/main/java/feign/SynchronousMethodHandler.java @@ -133,15 +133,15 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - for (ResponseInterceptor interceptor: responseInterceptors) { - interceptor.beforeDecode(response); + for (ResponseInterceptor interceptor : responseInterceptors) { + interceptor.beforeDecode(response); } if (decoder != null) { - Object object = decoder.decode(response, metadata.returnType()); + Object object = decoder.decode(response, metadata.returnType()); - for (ResponseInterceptor interceptor: responseInterceptors) { - interceptor.afterDecode(object); + for (ResponseInterceptor interceptor : responseInterceptors) { + interceptor.afterDecode(object); } return object; @@ -157,7 +157,7 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa throw new IllegalStateException("Response handling not done"); Object object = resultFuture.join(); - for (ResponseInterceptor interceptor: responseInterceptors) { + for (ResponseInterceptor interceptor : responseInterceptors) { interceptor.afterDecode(object); } return object; From 21d31bec98aad93a9f44a69d2ea42a2ad63fdb2b Mon Sep 17 00:00:00 2001 From: "Fei,Yanke" Date: Thu, 5 May 2022 12:20:16 +0800 Subject: [PATCH 5/5] combine before and after method to one aroundDecode method --- core/src/main/java/feign/Feign.java | 14 +++--- .../main/java/feign/ResponseInterceptor.java | 25 ++++------ .../java/feign/SynchronousMethodHandler.java | 50 +++++++++---------- 3 files changed, 40 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/feign/Feign.java b/core/src/main/java/feign/Feign.java index 371b7fa4c..f63b796a5 100644 --- a/core/src/main/java/feign/Feign.java +++ b/core/src/main/java/feign/Feign.java @@ -98,8 +98,7 @@ public static class Builder { private final List requestInterceptors = new ArrayList(); - private final List responseInterceptors = - new ArrayList(); + private ResponseInterceptor responseInterceptor = null; private Logger.Level logLevel = Logger.Level.NONE; private Contract contract = new Contract.Default(); private Client client = new Client.Default(null, null); @@ -233,7 +232,7 @@ public Builder requestInterceptor(RequestInterceptor requestInterceptor) { * Adds a single response interceptor to the builder. */ public Builder responseInterceptor(ResponseInterceptor responseInterceptor) { - this.responseInterceptors.add(responseInterceptor); + this.responseInterceptor = responseInterceptor; return this; } @@ -307,9 +306,6 @@ public Feign build() { List requestInterceptors = this.requestInterceptors.stream() .map(ri -> Capability.enrich(ri, capabilities)) .collect(Collectors.toList()); - List responseInterceptors = this.responseInterceptors.stream() - .map(ri -> Capability.enrich(ri, capabilities)) - .collect(Collectors.toList()); Logger logger = Capability.enrich(this.logger, capabilities); Contract contract = Capability.enrich(this.contract, capabilities); Options options = Capability.enrich(this.options, capabilities); @@ -318,10 +314,14 @@ public Feign build() { InvocationHandlerFactory invocationHandlerFactory = Capability.enrich(this.invocationHandlerFactory, capabilities); QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities); + ResponseInterceptor responseInterceptor = null; + if (this.responseInterceptor != null) { + responseInterceptor = Capability.enrich(this.responseInterceptor, capabilities); + } SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, - responseInterceptors, logger, logLevel, dismiss404, closeAfterDecode, + responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, diff --git a/core/src/main/java/feign/ResponseInterceptor.java b/core/src/main/java/feign/ResponseInterceptor.java index e1057967a..75ce6e0f9 100644 --- a/core/src/main/java/feign/ResponseInterceptor.java +++ b/core/src/main/java/feign/ResponseInterceptor.java @@ -13,29 +13,24 @@ */ package feign; +import java.util.function.Function; + /** - * Zero or more {@code ResponseInterceptor} may be configured for purposes such as verify or modify - * headers of response, verify the business status of decoded object. No guarantees are given with - * regards to the order that interceptors are applied. Once interceptors are applied, - * {@link ResponseInterceptor#beforeDecode(Response)} is called before decode method called, - * {@link ResponseInterceptor#afterDecode(Object)} is called after decode method called. + * Zero or One {@code ResponseInterceptor} may be configured for purposes such as verify or modify + * headers of response, verify the business status of decoded object. Once interceptors are applied, + * {@link ResponseInterceptor#aroundDecode(Response, Function)} is called around decode method + * called */ public interface ResponseInterceptor { /** - * Called for response before decode, add data on the supplied {@link Response} or doing - * customized logic + * Called for response around decode, user can use supplied {@link Function} decoder function to + * decode response data. * * @param response + * @param decoder * @return */ - void beforeDecode(Response response); + void aroundDecode(Response response, Function decoder); - /** - * Called for response after decode, add data to decoded object or doing customized logic - * - * @param response - * @return - */ - void afterDecode(Object response); } diff --git a/core/src/main/java/feign/SynchronousMethodHandler.java b/core/src/main/java/feign/SynchronousMethodHandler.java index c82ba000b..713db33b0 100644 --- a/core/src/main/java/feign/SynchronousMethodHandler.java +++ b/core/src/main/java/feign/SynchronousMethodHandler.java @@ -13,6 +13,9 @@ */ package feign; +import static feign.ExceptionPropagationPolicy.UNWRAP; +import static feign.FeignException.errorExecuting; +import static feign.Util.checkNotNull; import java.io.IOException; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -21,11 +24,9 @@ import java.util.stream.Stream; import feign.InvocationHandlerFactory.MethodHandler; import feign.Request.Options; +import feign.codec.DecodeException; import feign.codec.Decoder; import feign.codec.ErrorDecoder; -import static feign.ExceptionPropagationPolicy.UNWRAP; -import static feign.FeignException.errorExecuting; -import static feign.Util.checkNotNull; final class SynchronousMethodHandler implements MethodHandler { @@ -36,7 +37,7 @@ final class SynchronousMethodHandler implements MethodHandler { private final Client client; private final Retryer retryer; private final List requestInterceptors; - private final List responseInterceptors; + private final ResponseInterceptor responseInterceptor; private final Logger logger; private final Logger.Level logLevel; private final RequestTemplate.Factory buildTemplateFromArgs; @@ -49,7 +50,7 @@ final class SynchronousMethodHandler implements MethodHandler { private SynchronousMethodHandler(Target target, Client client, Retryer retryer, - List requestInterceptors, List responseInterceptors, + List requestInterceptors, ResponseInterceptor responseInterceptor, Logger logger, Logger.Level logLevel, MethodMetadata metadata, RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder, boolean dismiss404, @@ -61,14 +62,13 @@ private SynchronousMethodHandler(Target target, Client client, Retryer retrye this.retryer = checkNotNull(retryer, "retryer for %s", target); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors for %s", target); - this.responseInterceptors = - checkNotNull(responseInterceptors, "responseInterceptors for %s", target); this.logger = checkNotNull(logger, "logger for %s", target); this.logLevel = checkNotNull(logLevel, "logLevel for %s", target); this.metadata = checkNotNull(metadata, "metadata for %s", target); this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target); this.options = checkNotNull(options, "options for %s", target); this.propagationPolicy = propagationPolicy; + this.responseInterceptor = responseInterceptor; if (forceDecoding) { // internal only: usual handling will be short-circuited, and all responses will be passed to @@ -133,18 +133,19 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); - for (ResponseInterceptor interceptor : responseInterceptors) { - interceptor.beforeDecode(response); - } - if (decoder != null) { - Object object = decoder.decode(response, metadata.returnType()); - - for (ResponseInterceptor interceptor : responseInterceptors) { - interceptor.afterDecode(object); + if (responseInterceptor != null) { + responseInterceptor.aroundDecode(response, (res) -> { + try { + return decoder.decode(res, metadata.returnType()); + } catch (IOException e) { + throw new DecodeException(res.status(), "decode error cause of io exception", + res.request(), e); + } + }); + } else { + return decoder.decode(response, metadata.returnType()); } - - return object; } CompletableFuture resultFuture = new CompletableFuture<>(); @@ -155,12 +156,7 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa try { if (!resultFuture.isDone()) throw new IllegalStateException("Response handling not done"); - - Object object = resultFuture.join(); - for (ResponseInterceptor interceptor : responseInterceptors) { - interceptor.afterDecode(object); - } - return object; + return resultFuture.join(); } catch (CompletionException e) { Throwable cause = e.getCause(); if (cause != null) @@ -196,7 +192,7 @@ static class Factory { private final Client client; private final Retryer retryer; private final List requestInterceptors; - private final List responseInterceptors; + private final ResponseInterceptor responseInterceptor; private final Logger logger; private final Logger.Level logLevel; private final boolean dismiss404; @@ -205,13 +201,13 @@ static class Factory { private final boolean forceDecoding; Factory(Client client, Retryer retryer, List requestInterceptors, - List responseInterceptors, + ResponseInterceptor responseInterceptor, Logger logger, Logger.Level logLevel, boolean dismiss404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) { this.client = checkNotNull(client, "client"); this.retryer = checkNotNull(retryer, "retryer"); this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors"); - this.responseInterceptors = checkNotNull(responseInterceptors, "responseInterceptors"); + this.responseInterceptor = responseInterceptor; this.logger = checkNotNull(logger, "logger"); this.logLevel = checkNotNull(logLevel, "logLevel"); this.dismiss404 = dismiss404; @@ -227,7 +223,7 @@ public MethodHandler create(Target target, Decoder decoder, ErrorDecoder errorDecoder) { return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, - responseInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder, + responseInterceptor, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding); } }