All Downloads are FREE. Search and download functionalities are using the official Maven repository.

feign.RequestTemplate Maven / Gradle / Ivy

There is a newer version: 13.5
Show newest version
/*
 * Copyright 2012-2023 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;

import static feign.Util.CONTENT_LENGTH;
import static feign.Util.checkNotNull;
import feign.Request.HttpMethod;
import feign.template.BodyTemplate;
import feign.template.HeaderTemplate;
import feign.template.QueryTemplate;
import feign.template.UriTemplate;
import feign.template.UriUtils;
import java.io.Serializable;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Request Builder for an HTTP Target.
 * 

* This class is a variation on a UriTemplate, where, in addition to the uri, Headers and Query * information also support template expressions. *

*/ @SuppressWarnings("UnusedReturnValue") public final class RequestTemplate implements Serializable { private static final Pattern QUERY_STRING_PATTERN = Pattern.compile("(? queries = new LinkedHashMap<>(); private final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private String target; private String fragment; private boolean resolved = false; private UriTemplate uriTemplate; private BodyTemplate bodyTemplate; private HttpMethod method; private transient Charset charset = Util.UTF_8; private Request.Body body = Request.Body.empty(); private boolean decodeSlash = true; private CollectionFormat collectionFormat = CollectionFormat.EXPLODED; private MethodMetadata methodMetadata; private Target feignTarget; /** * Create a new Request Template. */ public RequestTemplate() { super(); } /** * Create a new Request Template. * * @param fragment part of the request uri. * @param target for the template. * @param uriTemplate for the template. * @param bodyTemplate for the template, may be {@literal null} * @param method of the request. * @param charset for the request. * @param body of the request, may be {@literal null} * @param decodeSlash if the request uri should encode slash characters. * @param collectionFormat when expanding collection based variables. * @param feignTarget this template is targeted for. * @param methodMetadata containing a reference to the method this template is built from. */ private RequestTemplate(String target, String fragment, UriTemplate uriTemplate, BodyTemplate bodyTemplate, HttpMethod method, Charset charset, Request.Body body, boolean decodeSlash, CollectionFormat collectionFormat, MethodMetadata methodMetadata, Target feignTarget) { this.target = target; this.fragment = fragment; this.uriTemplate = uriTemplate; this.bodyTemplate = bodyTemplate; this.method = method; this.charset = charset; this.body = body; this.decodeSlash = decodeSlash; this.collectionFormat = (collectionFormat != null) ? collectionFormat : CollectionFormat.EXPLODED; this.methodMetadata = methodMetadata; this.feignTarget = feignTarget; } /** * Create a Request Template from an existing Request Template. * * @param requestTemplate to copy from. * @return a new Request Template. */ public static RequestTemplate from(RequestTemplate requestTemplate) { RequestTemplate template = new RequestTemplate( requestTemplate.target, requestTemplate.fragment, requestTemplate.uriTemplate, requestTemplate.bodyTemplate, requestTemplate.method, requestTemplate.charset, requestTemplate.body, requestTemplate.decodeSlash, requestTemplate.collectionFormat, requestTemplate.methodMetadata, requestTemplate.feignTarget); if (!requestTemplate.queries().isEmpty()) { template.queries.putAll(requestTemplate.queries); } if (!requestTemplate.headers().isEmpty()) { template.headers.putAll(requestTemplate.headers); } return template; } /** * Create a Request Template from an existing Request Template. * * @param toCopy template. * @deprecated replaced by {@link RequestTemplate#from(RequestTemplate)} */ @Deprecated public RequestTemplate(RequestTemplate toCopy) { checkNotNull(toCopy, "toCopy"); this.target = toCopy.target; this.fragment = toCopy.fragment; this.method = toCopy.method; this.queries.putAll(toCopy.queries); this.headers.putAll(toCopy.headers); this.charset = toCopy.charset; this.body = toCopy.body; this.decodeSlash = toCopy.decodeSlash; this.collectionFormat = (toCopy.collectionFormat != null) ? toCopy.collectionFormat : CollectionFormat.EXPLODED; this.uriTemplate = toCopy.uriTemplate; this.bodyTemplate = toCopy.bodyTemplate; this.resolved = false; this.methodMetadata = toCopy.methodMetadata; this.target = toCopy.target; this.feignTarget = toCopy.feignTarget; } /** * Resolve all expressions using the variable value substitutions provided. Variable values will * be pct-encoded, if they are not already. * * @param variables containing the variable values to use when resolving expressions. * @return a new Request Template with all of the variables resolved. */ public RequestTemplate resolve(Map variables) { StringBuilder uri = new StringBuilder(); /* create a new template form this one, but explicitly */ RequestTemplate resolved = RequestTemplate.from(this); if (this.uriTemplate == null) { /* create a new uri template using the default root */ this.uriTemplate = UriTemplate.create("", !this.decodeSlash, this.charset); } String expanded = this.uriTemplate.expand(variables); if (expanded != null) { uri.append(expanded); } /* * for simplicity, combine the queries into the uri and use the resulting uri to seed the * resolved template. */ if (!this.queries.isEmpty()) { /* * since we only want to keep resolved query values, reset any queries on the resolved copy */ resolved.queries(Collections.emptyMap()); StringBuilder query = new StringBuilder(); Iterator queryTemplates = this.queries.values().iterator(); while (queryTemplates.hasNext()) { QueryTemplate queryTemplate = queryTemplates.next(); String queryExpanded = queryTemplate.expand(variables); if (Util.isNotBlank(queryExpanded)) { query.append(queryExpanded); if (queryTemplates.hasNext()) { query.append("&"); } } } String queryString = query.toString(); if (!queryString.isEmpty()) { Matcher queryMatcher = QUERY_STRING_PATTERN.matcher(uri); if (queryMatcher.find()) { /* the uri already has a query, so any additional queries should be appended */ uri.append("&"); } else { uri.append("?"); } uri.append(queryString); } } /* add the uri to result */ resolved.uri(uri.toString()); /* headers */ if (!this.headers.isEmpty()) { /* * same as the query string, we only want to keep resolved values, so clear the header map on * the resolved instance */ resolved.headers(Collections.emptyMap()); for (HeaderTemplate headerTemplate : this.headers.values()) { /* resolve the header */ String header = headerTemplate.expand(variables); if (!header.isEmpty()) { /* append the header as a new literal as the value has already been expanded. */ resolved.appendHeader( headerTemplate.getName(), Collections.singletonList(header), true); } } } if (this.bodyTemplate != null) { resolved.body(this.bodyTemplate.expand(variables)); } /* mark the new template resolved */ resolved.resolved = true; return resolved; } /** * Resolves all expressions, using the variables provided. Values not present in the {@code * alreadyEncoded} map are pct-encoded. * * @param unencoded variable values to substitute. * @param alreadyEncoded variable names. * @return a resolved Request Template * @deprecated use {@link RequestTemplate#resolve(Map)}. Values already encoded are recognized as * such and skipped. */ @SuppressWarnings("unused") @Deprecated RequestTemplate resolve(Map unencoded, Map alreadyEncoded) { return this.resolve(unencoded); } /** * Creates a {@link Request} from this template. The template must be resolved before calling this * method, or an {@link IllegalStateException} will be thrown. * * @return a new Request instance. * @throws IllegalStateException if this template has not been resolved. */ public Request request() { if (!this.resolved) { throw new IllegalStateException("template has not been resolved."); } return Request.create(this.method, this.url(), this.headers(), this.body, this); } /** * Set the Http Method. * * @param method to use. * @return a RequestTemplate for chaining. * @deprecated see {@link RequestTemplate#method(HttpMethod)} */ @Deprecated public RequestTemplate method(String method) { checkNotNull(method, "method"); try { this.method = HttpMethod.valueOf(method); } catch (IllegalArgumentException iae) { throw new IllegalArgumentException("Invalid HTTP Method: " + method); } return this; } /** * Set the Http Method. * * @param method to use. * @return a RequestTemplate for chaining. */ public RequestTemplate method(HttpMethod method) { checkNotNull(method, "method"); this.method = method; return this; } /** * The Request Http Method. * * @return Http Method. */ public String method() { return (method != null) ? method.name() : null; } /** * Set whether do encode slash {@literal /} characters when resolving this template. * * @param decodeSlash if slash literals should not be encoded. * @return a RequestTemplate for chaining. */ public RequestTemplate decodeSlash(boolean decodeSlash) { this.decodeSlash = decodeSlash; this.uriTemplate = UriTemplate.create(this.uriTemplate.toString(), !this.decodeSlash, this.charset); if (!this.queries.isEmpty()) { this.queries.replaceAll((key, queryTemplate) -> QueryTemplate.create( /* replace the current template with new ones honoring the decode value */ queryTemplate.getName(), queryTemplate.getValues(), charset, collectionFormat, decodeSlash)); } return this; } /** * If slash {@literal /} characters are not encoded when resolving. * * @return true if slash literals are not encoded, false otherwise. */ public boolean decodeSlash() { return decodeSlash; } /** * The Collection Format to use when resolving variables that represent {@link Iterable}s or * {@link Collection}s * * @param collectionFormat to use. * @return a RequestTemplate for chaining. */ public RequestTemplate collectionFormat(CollectionFormat collectionFormat) { this.collectionFormat = collectionFormat; return this; } /** * The Collection Format that will be used when resolving {@link Iterable} and {@link Collection} * variables. * * @return the collection format set */ @SuppressWarnings("unused") public CollectionFormat collectionFormat() { return collectionFormat; } /** * Append the value to the template. *

* This method is poorly named and is used primarily to store the relative uri for the request. It * has been replaced by {@link RequestTemplate#uri(String)} and will be removed in a future * release. *

* * @param value to append. * @return a RequestTemplate for chaining. * @deprecated see {@link RequestTemplate#uri(String, boolean)} */ @Deprecated public RequestTemplate append(CharSequence value) { /* proxy to url */ if (this.uriTemplate != null) { return this.uri(value.toString(), true); } return this.uri(value.toString()); } /** * Insert the value at the specified point in the template uri. *

* This method is poorly named has undocumented behavior. When the value contains a fully * qualified http request url, the value is always inserted at the beginning of the uri. *

*

* Due to this, use of this method is not recommended and remains for backward compatibility. It * has been replaced by {@link RequestTemplate#target(String)} and will be removed in a future * release. *

* * @param pos in the uri to place the value. * @param value to insert. * @return a RequestTemplate for chaining. * @deprecated see {@link RequestTemplate#target(String)} */ @SuppressWarnings("unused") @Deprecated public RequestTemplate insert(int pos, CharSequence value) { return target(value.toString()); } /** * Set the Uri for the request, replacing the existing uri if set. * * @param uri to use, must be a relative uri. * @return a RequestTemplate for chaining. */ public RequestTemplate uri(String uri) { return this.uri(uri, false); } /** * Set the uri for the request. * * @param uri to use, must be a relative uri. * @param append if the uri should be appended, if the uri is already set. * @return a RequestTemplate for chaining. */ public RequestTemplate uri(String uri, boolean append) { /* validate and ensure that the url is always a relative one */ if (UriUtils.isAbsolute(uri)) { throw new IllegalArgumentException("url values must be not be absolute."); } if (uri == null) { uri = "/"; } else if ((!uri.isEmpty() && !uri.startsWith("/") && !uri.startsWith("{") && !uri.startsWith("?") && !uri.startsWith(";"))) { /* if the start of the url is a literal, it must begin with a slash. */ uri = "/" + uri; } /* * templates may provide query parameters. since we want to manage those explicity, we will need * to extract those out, leaving the uriTemplate with only the path to deal with. */ Matcher queryMatcher = QUERY_STRING_PATTERN.matcher(uri); if (queryMatcher.find()) { String queryString = uri.substring(queryMatcher.start() + 1); /* parse the query string */ this.extractQueryTemplates(queryString, append); /* reduce the uri to the path */ uri = uri.substring(0, queryMatcher.start()); } int fragmentIndex = uri.indexOf('#'); if (fragmentIndex > -1) { fragment = uri.substring(fragmentIndex); uri = uri.substring(0, fragmentIndex); } /* replace the uri template */ if (append && this.uriTemplate != null) { this.uriTemplate = UriTemplate.append(this.uriTemplate, uri); } else { this.uriTemplate = UriTemplate.create(uri, !this.decodeSlash, this.charset); } return this; } /** * Set the target host for this request. * * @param target host for this request. Must be an absolute target. * @return a RequestTemplate for chaining. */ public RequestTemplate target(String target) { /* target can be empty */ if (Util.isBlank(target)) { return this; } /* verify that the target contains the scheme, host and port */ if (!UriUtils.isAbsolute(target)) { throw new IllegalArgumentException("target values must be absolute."); } if (target.endsWith("/")) { target = target.substring(0, target.length() - 1); } try { /* parse the target */ URI targetUri = URI.create(target); if (Util.isNotBlank(targetUri.getRawQuery())) { /* * target has a query string, we need to make sure that they are recorded as queries */ this.extractQueryTemplates(targetUri.getRawQuery(), true); } /* strip the query string */ this.target = targetUri.getScheme() + "://" + targetUri.getRawAuthority() + targetUri.getRawPath(); if (targetUri.getFragment() != null) { this.fragment = "#" + targetUri.getFragment(); } } catch (IllegalArgumentException iae) { /* the uri provided is not a valid one, we can't continue */ throw new IllegalArgumentException("Target is not a valid URI.", iae); } return this; } /** * The URL for the request. If the template has not been resolved, the url will represent a uri * template. * * @return the url */ public String url() { /* build the fully qualified url with all query parameters */ StringBuilder url = new StringBuilder(this.path()); if (!this.queries.isEmpty()) { url.append(this.queryLine()); } if (fragment != null) { url.append(fragment); } return url.toString(); } /** * The Uri Path. * * @return the uri path. */ public String path() { /* build the fully qualified url with all query parameters */ StringBuilder path = new StringBuilder(); if (this.target != null) { path.append(this.target); } if (this.uriTemplate != null) { path.append(this.uriTemplate.toString()); } if (path.length() == 0) { /* no path indicates the root uri */ path.append("/"); } return path.toString(); } /** * List all of the template variable expressions for this template. * * @return a list of template variable names */ public List variables() { /* combine the variables from the uri, query, header, and body templates */ List variables = new ArrayList<>(this.uriTemplate.getVariables()); /* queries */ for (QueryTemplate queryTemplate : this.queries.values()) { variables.addAll(queryTemplate.getVariables()); } /* headers */ for (HeaderTemplate headerTemplate : this.headers.values()) { variables.addAll(headerTemplate.getVariables()); } /* body */ if (this.bodyTemplate != null) { variables.addAll(this.bodyTemplate.getVariables()); } return variables; } /** * @see RequestTemplate#query(String, Iterable) */ public RequestTemplate query(String name, String... values) { if (values == null) { return query(name, Collections.emptyList()); } return query(name, Arrays.asList(values)); } /** * Specify a Query String parameter, with the specified values. Values can be literals or template * expressions. * * @param name of the parameter. * @param values for this parameter. * @return a RequestTemplate for chaining. */ public RequestTemplate query(String name, Iterable values) { return appendQuery(name, values, this.collectionFormat); } /** * Specify a Query String parameter, with the specified values. Values can be literals or template * expressions. * * @param name of the parameter. * @param values for this parameter. * @param collectionFormat to use when resolving collection based expressions. * @return a Request Template for chaining. */ public RequestTemplate query(String name, Iterable values, CollectionFormat collectionFormat) { return appendQuery(name, values, collectionFormat); } /** * Appends the query name and values. * * @param name of the parameter. * @param values for the parameter, may be expressions. * @param collectionFormat to use when resolving collection based query variables. * @return a RequestTemplate for chaining. */ private RequestTemplate appendQuery(String name, Iterable values, CollectionFormat collectionFormat) { if (!values.iterator().hasNext()) { /* empty value, clear the existing values */ this.queries.remove(name); return this; } /* create a new query template out of the information here */ this.queries.compute(name, (key, queryTemplate) -> { if (queryTemplate == null) { return QueryTemplate.create(name, values, this.charset, collectionFormat, this.decodeSlash); } else { return QueryTemplate.append(queryTemplate, values, collectionFormat, this.decodeSlash); } }); return this; } /** * Sets the Query Parameters. * * @param queries to use for this request. * @return a RequestTemplate for chaining. */ @SuppressWarnings("unused") public RequestTemplate queries(Map> queries) { if (queries == null || queries.isEmpty()) { this.queries.clear(); } else { queries.forEach(this::query); } return this; } /** * Return an immutable Map of all Query Parameters and their values. * * @return registered Query Parameters. */ public Map> queries() { Map> queryMap = new LinkedHashMap<>(); this.queries.forEach((key, queryTemplate) -> { List values = new ArrayList<>(queryTemplate.getValues()); /* add the expanded collection, but lock it */ queryMap.put(key, Collections.unmodifiableList(values)); }); return Collections.unmodifiableMap(queryMap); } /** * @see RequestTemplate#header(String, Iterable) */ public RequestTemplate header(String name, String... values) { return header(name, Arrays.asList(values)); } /** * Specify a Header, with the specified values. Values can be literals or template expressions. * * @param name of the header. * @param values for this header. * @return a RequestTemplate for chaining. */ public RequestTemplate header(String name, Iterable values) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("name is required."); } if (values == null) { values = Collections.emptyList(); } return appendHeader(name, values); } /** * Clear on reader from {@link RequestTemplate} * * @param name of the header. * @return a RequestTemplate for chaining. */ public RequestTemplate removeHeader(String name) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException("name is required."); } this.headers.remove(name); return this; } /** * Append the Header. Will create a new Header if it doesn't already exist. Treats all values as * potentially expressions. * * @param name of the header * @param values for the header, may be expressions. * @return a RequestTemplate for chaining. */ private RequestTemplate appendHeader(String name, Iterable values) { return this.appendHeader(name, values, false); } /** * Append the Header. Will create a new Header if it doesn't already exist. * * @param name of the header * @param values for the header, may be expressions. * @param literal indicator, to treat the values as literals and not expressions * @return a RequestTemplate for chaining. */ private RequestTemplate appendHeader(String name, Iterable values, boolean literal) { if (!values.iterator().hasNext()) { /* empty value, clear the existing values */ this.headers.remove(name); return this; } if (name.equals("Content-Type")) { // a client can only produce content of one single type, so always override Content-Type and // only add a single type this.headers.remove(name); this.headers.put(name, HeaderTemplate.create(name, Collections.singletonList(values.iterator().next()))); return this; } this.headers.compute(name, (headerName, headerTemplate) -> { if (headerTemplate == null) { if (literal) { return HeaderTemplate.literal(headerName, values); } else { return HeaderTemplate.create(headerName, values); } } else if (literal) { return HeaderTemplate.appendLiteral(headerTemplate, values); } else { return HeaderTemplate.append(headerTemplate, values); } }); return this; } /** * Headers for this Request. * * @param headers to use. * @return a RequestTemplate for chaining. */ public RequestTemplate headers(Map> headers) { if (headers != null && !headers.isEmpty()) { headers.forEach(this::header); } else { this.headers.clear(); } return this; } /** * Returns an copy of the Headers for this request. * * @return the currently applied headers. */ public Map> headers() { Map> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); this.headers.forEach((key, headerTemplate) -> { List values = new ArrayList<>(headerTemplate.getValues()); /* add the expanded collection, but only if it has values */ if (!values.isEmpty()) { headerMap.put(key, values); } }); return headerMap; } /** * Sets the Body and Charset for this request. * * @param data to send, can be null. * @param charset of the encoded data. * @return a RequestTemplate for chaining. */ public RequestTemplate body(byte[] data, Charset charset) { this.body(Request.Body.create(data, charset)); return this; } /** * Set the Body for this request. Charset is assumed to be UTF_8. Data must be encoded. * * @param bodyText to send. * @return a RequestTemplate for chaining. */ public RequestTemplate body(String bodyText) { this.body(Request.Body.create(bodyText.getBytes(this.charset), this.charset)); return this; } /** * Set the Body for this request. * * @param body to send. * @return a RequestTemplate for chaining. * @deprecated use {@link #body(byte[], Charset)} instead. */ @Deprecated public RequestTemplate body(Request.Body body) { this.body = body; /* body template must be cleared to prevent double processing */ this.bodyTemplate = null; header(CONTENT_LENGTH, Collections.emptyList()); if (body.length() > 0) { header(CONTENT_LENGTH, String.valueOf(body.length())); } return this; } /** * Charset of the Request Body, if known. * * @return the currently applied Charset. */ public Charset requestCharset() { if (this.body != null) { return this.body.getEncoding() .orElse(this.charset); } return this.charset; } /** * The Request Body. * * @return the request body. */ public byte[] body() { return body.asBytes(); } /** * The Request.Body internal object. * * @return the internal Request.Body. * @deprecated this abstraction is leaky and will be removed in later releases. */ @Deprecated public Request.Body requestBody() { return this.body; } /** * Specify the Body Template to use. Can contain literals and expressions. * * @param bodyTemplate to use. * @return a RequestTemplate for chaining. */ public RequestTemplate bodyTemplate(String bodyTemplate) { this.bodyTemplate = BodyTemplate.create(bodyTemplate, this.charset); return this; } /** * Specify the Body Template to use. Can contain literals and expressions. * * @param bodyTemplate to use. * @return a RequestTemplate for chaining. */ public RequestTemplate bodyTemplate(String bodyTemplate, Charset charset) { this.bodyTemplate = BodyTemplate.create(bodyTemplate, charset); this.charset = charset; return this; } /** * Body Template to resolve. * * @return the unresolved body template. */ public String bodyTemplate() { if (this.bodyTemplate != null) { return this.bodyTemplate.toString(); } return null; } @Override public String toString() { return request().toString(); } /** * Return if the variable exists on the uri, query, or headers, in this template. * * @param variable to look for. * @return true if the variable exists, false otherwise. */ public boolean hasRequestVariable(String variable) { return this.getRequestVariables().contains(variable); } /** * Retrieve all uri, header, and query template variables. * * @return a List of all the variable names. */ public Collection getRequestVariables() { final Collection variables = new LinkedHashSet<>(this.uriTemplate.getVariables()); this.queries.values().forEach(queryTemplate -> variables.addAll(queryTemplate.getVariables())); this.headers.values() .forEach(headerTemplate -> variables.addAll(headerTemplate.getVariables())); return variables; } /** * If this template has been resolved. * * @return true if the template has been resolved, false otherwise. */ @SuppressWarnings("unused") public boolean resolved() { return this.resolved; } /** * The Query String for the template. Expressions are not resolved. * * @return the Query String. */ public String queryLine() { StringBuilder queryString = new StringBuilder(); if (!this.queries.isEmpty()) { Iterator iterator = this.queries.values().iterator(); while (iterator.hasNext()) { QueryTemplate queryTemplate = iterator.next(); String query = queryTemplate.toString(); if (query != null && !query.isEmpty()) { queryString.append(query); if (iterator.hasNext()) { queryString.append("&"); } } } } /* remove any trailing ampersands */ String result = queryString.toString(); if (result.endsWith("&")) { result = result.substring(0, result.length() - 1); } if (!result.isEmpty()) { result = "?" + result; } return result; } private void extractQueryTemplates(String queryString, boolean append) { /* split the query string up into name value pairs */ Map> queryParameters = Arrays.stream(queryString.split("&")) .map(this::splitQueryParameter) .collect(Collectors.groupingBy( SimpleImmutableEntry::getKey, LinkedHashMap::new, Collectors.mapping(Entry::getValue, Collectors.toList()))); /* add them to this template */ if (!append) { /* clear the queries and use the new ones */ this.queries.clear(); } queryParameters.forEach(this::query); } private SimpleImmutableEntry splitQueryParameter(String pair) { int eq = pair.indexOf("="); final String name = (eq > 0) ? pair.substring(0, eq) : pair; final String value = (eq > 0 && eq < pair.length()) ? pair.substring(eq + 1) : null; return new SimpleImmutableEntry<>(name, value); } @Experimental public RequestTemplate methodMetadata(MethodMetadata methodMetadata) { this.methodMetadata = methodMetadata; return this; } @Experimental public RequestTemplate feignTarget(Target feignTarget) { this.feignTarget = feignTarget; return this; } @Experimental public MethodMetadata methodMetadata() { return methodMetadata; } @Experimental public Target feignTarget() { return feignTarget; } /** * Factory for creating RequestTemplate. */ interface Factory { /** * create a request template using args passed to a method invocation. */ RequestTemplate create(Object[] argv); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy