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

org.springframework.web.util.DefaultUriBuilderFactory Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2020 the original author or 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
 *
 *      https://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 org.springframework.web.util;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * {@code UriBuilderFactory} that relies on {@link UriComponentsBuilder} for
 * the actual building of the URI.
 *
 * 

Provides options to create {@link UriBuilder} instances with a common * base URI, alternative encoding mode strategies, among others. * * @author Rossen Stoyanchev * @since 5.0 * @see UriComponentsBuilder */ public class DefaultUriBuilderFactory implements UriBuilderFactory { @Nullable private final UriComponentsBuilder baseUri; private EncodingMode encodingMode = EncodingMode.TEMPLATE_AND_VALUES; private final Map defaultUriVariables = new HashMap<>(); private boolean parsePath = true; /** * Default constructor without a base URI. *

The target address must be specified on each UriBuilder. */ public DefaultUriBuilderFactory() { this.baseUri = null; } /** * Constructor with a base URI. *

The given URI template is parsed via * {@link UriComponentsBuilder#fromUriString} and then applied as a base URI * to every UriBuilder via {@link UriComponentsBuilder#uriComponents} unless * the UriBuilder itself was created with a URI template that already has a * target address. * @param baseUriTemplate the URI template to use a base URL */ public DefaultUriBuilderFactory(String baseUriTemplate) { this.baseUri = UriComponentsBuilder.fromUriString(baseUriTemplate); } /** * Variant of {@link #DefaultUriBuilderFactory(String)} with a * {@code UriComponentsBuilder}. */ public DefaultUriBuilderFactory(UriComponentsBuilder baseUri) { this.baseUri = baseUri; } /** * Set the {@link EncodingMode encoding mode} to use. *

By default this is set to {@link EncodingMode#TEMPLATE_AND_VALUES * EncodingMode.TEMPLATE_AND_VALUES}. *

Note: Prior to 5.1 the default was * {@link EncodingMode#URI_COMPONENT EncodingMode.URI_COMPONENT} * therefore the {@code WebClient} {@code RestTemplate} have switched their * default behavior. * @param encodingMode the encoding mode to use */ public void setEncodingMode(EncodingMode encodingMode) { this.encodingMode = encodingMode; } /** * Return the configured encoding mode. */ public EncodingMode getEncodingMode() { return this.encodingMode; } /** * Provide default URI variable values to use when expanding URI templates * with a Map of variables. * @param defaultUriVariables default URI variable values */ public void setDefaultUriVariables(@Nullable Map defaultUriVariables) { this.defaultUriVariables.clear(); if (defaultUriVariables != null) { this.defaultUriVariables.putAll(defaultUriVariables); } } /** * Return the configured default URI variable values. */ public Map getDefaultUriVariables() { return Collections.unmodifiableMap(this.defaultUriVariables); } /** * Whether to parse the input path into path segments if the encoding mode * is set to {@link EncodingMode#URI_COMPONENT EncodingMode.URI_COMPONENT}, * which ensures that URI variables in the path are encoded according to * path segment rules and for example a '/' is encoded. *

By default this is set to {@code true}. * @param parsePath whether to parse the path into path segments */ public void setParsePath(boolean parsePath) { this.parsePath = parsePath; } /** * Whether to parse the path into path segments if the encoding mode is set * to {@link EncodingMode#URI_COMPONENT EncodingMode.URI_COMPONENT}. */ public boolean shouldParsePath() { return this.parsePath; } // UriTemplateHandler @Override public URI expand(String uriTemplate, Map uriVars) { return uriString(uriTemplate).build(uriVars); } @Override public URI expand(String uriTemplate, Object... uriVars) { return uriString(uriTemplate).build(uriVars); } // UriBuilderFactory @Override public UriBuilder uriString(String uriTemplate) { return new DefaultUriBuilder(uriTemplate); } @Override public UriBuilder builder() { return new DefaultUriBuilder(""); } /** * Enum to represent multiple URI encoding strategies. The following are * available: *

    *
  • {@link #TEMPLATE_AND_VALUES} *
  • {@link #VALUES_ONLY} *
  • {@link #URI_COMPONENT} *
  • {@link #NONE} *
* @see #setEncodingMode */ public enum EncodingMode { /** * Pre-encode the URI template first, then strictly encode URI variables * when expanded, with the following rules: *
    *
  • For the URI template replace only non-ASCII and illegal * (within a given URI component type) characters with escaped octets. *
  • For URI variables do the same and also replace characters with * reserved meaning. *
*

For most cases, this mode is most likely to give the expected * result because in treats URI variables as opaque data to be fully * encoded, while {@link #URI_COMPONENT} by comparison is useful only * if intentionally expanding URI variables with reserved characters. * @since 5.0.8 * @see UriComponentsBuilder#encode() */ TEMPLATE_AND_VALUES, /** * Does not encode the URI template and instead applies strict encoding * to URI variables via {@link UriUtils#encodeUriVariables} prior to * expanding them into the template. * @see UriUtils#encodeUriVariables(Object...) * @see UriUtils#encodeUriVariables(Map) */ VALUES_ONLY, /** * Expand URI variables first, and then encode the resulting URI * component values, replacing only non-ASCII and illegal * (within a given URI component type) characters, but not characters * with reserved meaning. * @see UriComponents#encode() */ URI_COMPONENT, /** * No encoding should be applied. */ NONE } /** * {@link DefaultUriBuilderFactory} specific implementation of UriBuilder. */ private class DefaultUriBuilder implements UriBuilder { private final UriComponentsBuilder uriComponentsBuilder; public DefaultUriBuilder(String uriTemplate) { this.uriComponentsBuilder = initUriComponentsBuilder(uriTemplate); } private UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) { UriComponentsBuilder result; if (!StringUtils.hasLength(uriTemplate)) { result = (baseUri != null ? baseUri.cloneBuilder() : UriComponentsBuilder.newInstance()); } else if (baseUri != null) { UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate); UriComponents uri = builder.build(); result = (uri.getHost() == null ? baseUri.cloneBuilder().uriComponents(uri) : builder); } else { result = UriComponentsBuilder.fromUriString(uriTemplate); } if (encodingMode.equals(EncodingMode.TEMPLATE_AND_VALUES)) { result.encode(); } parsePathIfNecessary(result); return result; } private void parsePathIfNecessary(UriComponentsBuilder result) { if (parsePath && encodingMode.equals(EncodingMode.URI_COMPONENT)) { UriComponents uric = result.build(); String path = uric.getPath(); result.replacePath(null); for (String segment : uric.getPathSegments()) { result.pathSegment(segment); } if (path != null && path.endsWith("/")) { result.path("/"); } } } @Override public DefaultUriBuilder scheme(@Nullable String scheme) { this.uriComponentsBuilder.scheme(scheme); return this; } @Override public DefaultUriBuilder userInfo(@Nullable String userInfo) { this.uriComponentsBuilder.userInfo(userInfo); return this; } @Override public DefaultUriBuilder host(@Nullable String host) { this.uriComponentsBuilder.host(host); return this; } @Override public DefaultUriBuilder port(int port) { this.uriComponentsBuilder.port(port); return this; } @Override public DefaultUriBuilder port(@Nullable String port) { this.uriComponentsBuilder.port(port); return this; } @Override public DefaultUriBuilder path(String path) { this.uriComponentsBuilder.path(path); return this; } @Override public DefaultUriBuilder replacePath(@Nullable String path) { this.uriComponentsBuilder.replacePath(path); return this; } @Override public DefaultUriBuilder pathSegment(String... pathSegments) { this.uriComponentsBuilder.pathSegment(pathSegments); return this; } @Override public DefaultUriBuilder query(String query) { this.uriComponentsBuilder.query(query); return this; } @Override public DefaultUriBuilder replaceQuery(@Nullable String query) { this.uriComponentsBuilder.replaceQuery(query); return this; } @Override public DefaultUriBuilder queryParam(String name, Object... values) { this.uriComponentsBuilder.queryParam(name, values); return this; } @Override public DefaultUriBuilder queryParam(String name, @Nullable Collection values) { this.uriComponentsBuilder.queryParam(name, values); return this; } @Override public DefaultUriBuilder queryParamIfPresent(String name, Optional value) { this.uriComponentsBuilder.queryParamIfPresent(name, value); return this; } @Override public DefaultUriBuilder queryParams(MultiValueMap params) { this.uriComponentsBuilder.queryParams(params); return this; } @Override public DefaultUriBuilder replaceQueryParam(String name, Object... values) { this.uriComponentsBuilder.replaceQueryParam(name, values); return this; } @Override public DefaultUriBuilder replaceQueryParam(String name, @Nullable Collection values) { this.uriComponentsBuilder.replaceQueryParam(name, values); return this; } @Override public DefaultUriBuilder replaceQueryParams(MultiValueMap params) { this.uriComponentsBuilder.replaceQueryParams(params); return this; } @Override public DefaultUriBuilder fragment(@Nullable String fragment) { this.uriComponentsBuilder.fragment(fragment); return this; } @Override public URI build(Map uriVars) { if (!defaultUriVariables.isEmpty()) { Map map = new HashMap<>(); map.putAll(defaultUriVariables); map.putAll(uriVars); uriVars = map; } if (encodingMode.equals(EncodingMode.VALUES_ONLY)) { uriVars = UriUtils.encodeUriVariables(uriVars); } UriComponents uric = this.uriComponentsBuilder.build().expand(uriVars); return createUri(uric); } @Override public URI build(Object... uriVars) { if (ObjectUtils.isEmpty(uriVars) && !defaultUriVariables.isEmpty()) { return build(Collections.emptyMap()); } if (encodingMode.equals(EncodingMode.VALUES_ONLY)) { uriVars = UriUtils.encodeUriVariables(uriVars); } UriComponents uric = this.uriComponentsBuilder.build().expand(uriVars); return createUri(uric); } private URI createUri(UriComponents uric) { if (encodingMode.equals(EncodingMode.URI_COMPONENT)) { uric = uric.encode(); } return URI.create(uric.toString()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy