Handle removing trailing slash from path.

This commit is contained in:
Olga Maciaszek-Sharma
2024-10-09 19:13:51 +02:00
parent 560239a92b
commit 1c22ebe06b
4 changed files with 60 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2024 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.
@@ -61,6 +61,12 @@ public class FeignClientProperties {
*/
private boolean decodeSlash = true;
/**
* If {@code true}, trailing slashes at the end
* of request urls will be removed.
*/
private boolean removeTrailingSlash;
public boolean isDefaultToProperties() {
return defaultToProperties;
}
@@ -93,6 +99,14 @@ public class FeignClientProperties {
this.decodeSlash = decodeSlash;
}
public boolean isRemoveTrailingSlash() {
return removeTrailingSlash;
}
public void setRemoveTrailingSlash(boolean removeTrailingSlash) {
this.removeTrailingSlash = removeTrailingSlash;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -103,12 +117,13 @@ public class FeignClientProperties {
}
FeignClientProperties that = (FeignClientProperties) o;
return defaultToProperties == that.defaultToProperties && Objects.equals(defaultConfig, that.defaultConfig)
&& Objects.equals(config, that.config) && Objects.equals(decodeSlash, that.decodeSlash);
&& Objects.equals(config, that.config) && Objects.equals(decodeSlash, that.decodeSlash)
&& Objects.equals(removeTrailingSlash, that.removeTrailingSlash);
}
@Override
public int hashCode() {
return Objects.hash(defaultToProperties, defaultConfig, config, decodeSlash);
return Objects.hash(defaultToProperties, defaultConfig, config, decodeSlash, removeTrailingSlash);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2024 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.
@@ -145,8 +145,7 @@ public class FeignClientsConfiguration {
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash();
return new SpringMvcContract(parameterProcessors, feignConversionService, decodeSlash);
return new SpringMvcContract(parameterProcessors, feignConversionService, feignClientProperties);
}
@Bean

View File

@@ -41,6 +41,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.CollectionFormat;
import org.springframework.cloud.openfeign.FeignClientProperties;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.cloud.openfeign.annotation.CookieValueParameterProcessor;
import org.springframework.cloud.openfeign.annotation.MatrixVariableParameterProcessor;
@@ -115,6 +116,8 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
private final boolean decodeSlash;
private final boolean removeTrailingSlash;
public SpringMvcContract() {
this(Collections.emptyList());
}
@@ -128,8 +131,32 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
this(annotatedParameterProcessors, conversionService, true);
}
/**
* Creates a {@link SpringMvcContract} based on annotatedParameterProcessors,
* conversionService and decodeSlash value.
* @param annotatedParameterProcessors list of {@link AnnotatedParameterProcessor} objects used to resolve parameters
* @param conversionService {@link ConversionService} used for type conversion
* @param decodeSlash indicates whether slashes should be decoded
* @deprecated in favour of {@link SpringMvcContract#SpringMvcContract(List, ConversionService, FeignClientProperties)}
*/
@Deprecated
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
ConversionService conversionService, boolean decodeSlash) {
this(annotatedParameterProcessors, conversionService, decodeSlash, false);
}
/**
* Creates a {@link SpringMvcContract} based on annotatedParameterProcessors,
* conversionService and decodeSlash value.
* @param annotatedParameterProcessors list of {@link AnnotatedParameterProcessor} objects used to resolve parameters
* @param conversionService {@link ConversionService} used for type conversion
* @param decodeSlash indicates whether slashes should be decoded
* @param removeTrailingSlash indicates whether trailing slashes should be removed
* @deprecated in favour of {@link SpringMvcContract#SpringMvcContract(List, ConversionService, FeignClientProperties)}
*/
@Deprecated
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
ConversionService conversionService, boolean decodeSlash, boolean removeTrailingSlash) {
Assert.notNull(annotatedParameterProcessors, "Parameter processors can not be null.");
Assert.notNull(conversionService, "ConversionService can not be null.");
@@ -140,6 +167,14 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
this.conversionService = conversionService;
convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
this.decodeSlash = decodeSlash;
this.removeTrailingSlash = removeTrailingSlash;
}
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
ConversionService conversionService, FeignClientProperties feignClientProperties) {
this(annotatedParameterProcessors, conversionService,
feignClientProperties == null || feignClientProperties.isDecodeSlash(),
feignClientProperties != null && feignClientProperties.isRemoveTrailingSlash());
}
private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
@@ -229,6 +264,9 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
pathValue = "/" + pathValue;
}
if (removeTrailingSlash && pathValue.endsWith("/")) {
pathValue = pathValue.substring(0, pathValue.length() - 1);
}
data.template().uri(pathValue, true);
if (data.template().decodeSlash() != decodeSlash) {
data.template().decodeSlash(decodeSlash);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2024 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.
@@ -90,7 +90,7 @@ class FeignClientsRegistrarTests {
}
@Test
void removeLastSlashOfUrl() {
void testRemoveTrailingSlashFromUrl() {
String url = FeignClientsRegistrar.getUrl("http://localhost/");
assertThat(url).isEqualTo("http://localhost");
}