Skip to content

Commit 1c22ebe

Browse files
committed
Handle removing trailing slash from path.
1 parent 560239a commit 1c22ebe

File tree

4 files changed

+60
-8
lines changed

4 files changed

+60
-8
lines changed

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2023 the original author or authors.
2+
* Copyright 2013-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,12 @@ public class FeignClientProperties {
6161
*/
6262
private boolean decodeSlash = true;
6363

64+
/**
65+
* If {@code true}, trailing slashes at the end
66+
* of request urls will be removed.
67+
*/
68+
private boolean removeTrailingSlash;
69+
6470
public boolean isDefaultToProperties() {
6571
return defaultToProperties;
6672
}
@@ -93,6 +99,14 @@ public void setDecodeSlash(boolean decodeSlash) {
9399
this.decodeSlash = decodeSlash;
94100
}
95101

102+
public boolean isRemoveTrailingSlash() {
103+
return removeTrailingSlash;
104+
}
105+
106+
public void setRemoveTrailingSlash(boolean removeTrailingSlash) {
107+
this.removeTrailingSlash = removeTrailingSlash;
108+
}
109+
96110
@Override
97111
public boolean equals(Object o) {
98112
if (this == o) {
@@ -103,12 +117,13 @@ public boolean equals(Object o) {
103117
}
104118
FeignClientProperties that = (FeignClientProperties) o;
105119
return defaultToProperties == that.defaultToProperties && Objects.equals(defaultConfig, that.defaultConfig)
106-
&& Objects.equals(config, that.config) && Objects.equals(decodeSlash, that.decodeSlash);
120+
&& Objects.equals(config, that.config) && Objects.equals(decodeSlash, that.decodeSlash)
121+
&& Objects.equals(removeTrailingSlash, that.removeTrailingSlash);
107122
}
108123

109124
@Override
110125
public int hashCode() {
111-
return Objects.hash(defaultToProperties, defaultConfig, config, decodeSlash);
126+
return Objects.hash(defaultToProperties, defaultConfig, config, decodeSlash, removeTrailingSlash);
112127
}
113128

114129
/**

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2022 the original author or authors.
2+
* Copyright 2013-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -145,8 +145,7 @@ public QueryMapEncoder feignQueryMapEncoderPageable() {
145145
@Bean
146146
@ConditionalOnMissingBean
147147
public Contract feignContract(ConversionService feignConversionService) {
148-
boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash();
149-
return new SpringMvcContract(parameterProcessors, feignConversionService, decodeSlash);
148+
return new SpringMvcContract(parameterProcessors, feignConversionService, feignClientProperties);
150149
}
151150

152151
@Bean

spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
4343
import org.springframework.cloud.openfeign.CollectionFormat;
44+
import org.springframework.cloud.openfeign.FeignClientProperties;
4445
import org.springframework.cloud.openfeign.SpringQueryMap;
4546
import org.springframework.cloud.openfeign.annotation.CookieValueParameterProcessor;
4647
import org.springframework.cloud.openfeign.annotation.MatrixVariableParameterProcessor;
@@ -115,6 +116,8 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
115116

116117
private final boolean decodeSlash;
117118

119+
private final boolean removeTrailingSlash;
120+
118121
public SpringMvcContract() {
119122
this(Collections.emptyList());
120123
}
@@ -128,8 +131,32 @@ public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterPro
128131
this(annotatedParameterProcessors, conversionService, true);
129132
}
130133

134+
/**
135+
* Creates a {@link SpringMvcContract} based on annotatedParameterProcessors,
136+
* conversionService and decodeSlash value.
137+
* @param annotatedParameterProcessors list of {@link AnnotatedParameterProcessor} objects used to resolve parameters
138+
* @param conversionService {@link ConversionService} used for type conversion
139+
* @param decodeSlash indicates whether slashes should be decoded
140+
* @deprecated in favour of {@link SpringMvcContract#SpringMvcContract(List, ConversionService, FeignClientProperties)}
141+
*/
142+
@Deprecated
131143
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
132144
ConversionService conversionService, boolean decodeSlash) {
145+
this(annotatedParameterProcessors, conversionService, decodeSlash, false);
146+
}
147+
148+
/**
149+
* Creates a {@link SpringMvcContract} based on annotatedParameterProcessors,
150+
* conversionService and decodeSlash value.
151+
* @param annotatedParameterProcessors list of {@link AnnotatedParameterProcessor} objects used to resolve parameters
152+
* @param conversionService {@link ConversionService} used for type conversion
153+
* @param decodeSlash indicates whether slashes should be decoded
154+
* @param removeTrailingSlash indicates whether trailing slashes should be removed
155+
* @deprecated in favour of {@link SpringMvcContract#SpringMvcContract(List, ConversionService, FeignClientProperties)}
156+
*/
157+
@Deprecated
158+
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
159+
ConversionService conversionService, boolean decodeSlash, boolean removeTrailingSlash) {
133160
Assert.notNull(annotatedParameterProcessors, "Parameter processors can not be null.");
134161
Assert.notNull(conversionService, "ConversionService can not be null.");
135162

@@ -140,6 +167,14 @@ public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterPro
140167
this.conversionService = conversionService;
141168
convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
142169
this.decodeSlash = decodeSlash;
170+
this.removeTrailingSlash = removeTrailingSlash;
171+
}
172+
173+
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
174+
ConversionService conversionService, FeignClientProperties feignClientProperties) {
175+
this(annotatedParameterProcessors, conversionService,
176+
feignClientProperties == null || feignClientProperties.isDecodeSlash(),
177+
feignClientProperties != null && feignClientProperties.isRemoveTrailingSlash());
143178
}
144179

145180
private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
@@ -229,6 +264,9 @@ protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodA
229264
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
230265
pathValue = "/" + pathValue;
231266
}
267+
if (removeTrailingSlash && pathValue.endsWith("/")) {
268+
pathValue = pathValue.substring(0, pathValue.length() - 1);
269+
}
232270
data.template().uri(pathValue, true);
233271
if (data.template().decodeSlash() != decodeSlash) {
234272
data.template().decodeSlash(decodeSlash);

spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2022 the original author or authors.
2+
* Copyright 2013-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -90,7 +90,7 @@ private String testGetName(String name) {
9090
}
9191

9292
@Test
93-
void removeLastSlashOfUrl() {
93+
void testRemoveTrailingSlashFromUrl() {
9494
String url = FeignClientsRegistrar.getUrl("http://localhost/");
9595
assertThat(url).isEqualTo("http://localhost");
9696
}

0 commit comments

Comments
 (0)