Feign利用自定義注解實現(xiàn)路徑轉義詳解
背景
近期由于項目中需要,所以需要通過Feign封裝一個對Harbor操作的sdk信息。
在調(diào)用的過程中發(fā)現(xiàn),當請求參數(shù)中帶有"/"時,F(xiàn)eign默認會將"/"當成路徑去解析,而不是當成完整的一個參數(shù)解析,實例如下
請求路徑為:api/v2.0/projects/{projectName}/repositories
注解參數(shù)為:@PathVariable("projectName")
正常請求為:api/v2.0/projects/test/repositories
異常路徑為:api/v2.0/projects/test/pro/repositories
相信細心的同學已經(jīng)發(fā)現(xiàn)上面的差異了,正常的{projectName}中對應的值為test,而異常的卻對應為test/pro,所以當異常的請求打到harbor的機器時,被解析為api/v2.0/projects/test/pro/repositories,所以會直接返回404
以上就是背景了,所以接下來我們討論一下解決方案
解決方案
首先我們知道springboot中默認是帶有幾種注釋參數(shù)處理器的
@MatrixVariableParameterProcessor @PathVariableParameterProcessor @QueryMapParameterProcessor @RequestHeaderParameterProcessor @RequestParamParameterProcessor @RequestPartParameterProcessor
因為我們的請求參數(shù)是在路徑中的,所以默認我們會使用@PathVariableParameterProcessor來標識路徑參數(shù),而我們需要轉義的參數(shù)其實也是在路徑中,所以我們先來看一下@PathVariableParameterProcessor是如何實現(xiàn)的
public boolean processArgument(AnnotatedParameterProcessor.AnnotatedParameterContext context, Annotation annotation, Method method) {
String name = ((PathVariable)ANNOTATION.cast(annotation)).value();
Util.checkState(Util.emptyToNull(name) != null, "PathVariable annotation was empty on param %s.", new Object[]{context.getParameterIndex()});
context.setParameterName(name);
MethodMetadata data = context.getMethodMetadata();
String varName = '{' + name + '}';
if (!data.template().url().contains(varName) && !this.searchMapValues(data.template().queries(), varName) && !this.searchMapValues(data.template().headers(), varName)) {
data.formParams().add(name);
}
return true;
}
其實在源碼中,springboot并沒有做什么神器的事情,就是獲取使用了PathVariable注解的參數(shù),然后再將其添加到fromParams中就可以。
看到這里我們是不是可以想到,既然在這里我們可以拿到對應的參數(shù)了,那想做什么事情不都是由我們自己來決定了,接下來說干就干,
首先我們聲明一個屬于自己的注解,
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* @CreateAt: 2022/6/11 0:46
* @ModifyAt: 2022/6/11 0:46
* @Version 1.0
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SlashPathVariable {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the path variable to bind to.
* @since 4.3.3
*/
@AliasFor("value")
String name() default "";
/**
* Whether the path variable is required.
* <p>Defaults to {@code true}, leading to an exception being thrown if the path
* variable is missing in the incoming request. Switch this to {@code false} if
* you prefer a {@code null} or Java 8 {@code java.util.Optional} in this case.
* e.g. on a {@code ModelAttribute} method which serves for different requests.
* @since 4.3.3
*/
boolean required() default true;
}
聲明完注解后,我們就需要來自定義自己的參數(shù)解析器了,首先繼承AnnotatedParameterProcessor
import feign.MethodMetadata;
import feign.Util;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.PathVariable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @CreateAt: 2022/6/11 0:36
* @ModifyAt: 2022/6/11 0:36
* @Version 1.0
*/
public class SlashPathVariableParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<SlashPathVariable> ANNOTATION=SlashPathVariable.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return (Class<? extends Annotation>) ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
MethodMetadata data = context.getMethodMetadata();
String name = ANNOTATION.cast(annotation).value();
Util.checkState(Util.emptyToNull(name) != null, "SlashPathVariable annotation was empty on param %s.", new Object[]{context.getParameterIndex()});
context.setParameterName(name);
data.indexToExpander().put(context.getParameterIndex(),this::expandMap);
return true;
}
private String expandMap(Object object) {
String encode = URLEncoder.encode(URLEncoder.encode(object.toString(), Charset.defaultCharset()), Charset.defaultCharset());
return encode;
}
}
可以看到上面的代碼,我們獲取到自定義注解的參數(shù)后,將當前參數(shù)添加打Param后,并且為當前參數(shù)指定自定義的編碼格式。
最后,我們再通過Bean的形式將對應的注解添加到容器中
import feign.Contract;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @CreateAt: 2022/6/11 0:48
* @ModifyAt: 2022/6/11 0:48
* @Version 1.0
*/
@Component
public class SlashBean {
@Bean
public Contract feignContract(){
List<AnnotatedParameterProcessor> processors=new ArrayList<>();
processors.add(new SlashPathVariableParameterProcessor());
return new SpringMvcContract(processors);
}
}
最后我們將上面的參數(shù)注解PathVariable換成我們自定義的@SlashPathVariable,就大功告成了
最后
通過以上的形式進行注入的話,會注入到Spring全局,所以在使用的過程中需要考慮是否符合場景
以上就是Feign利用自定義注解實現(xiàn)路徑的轉義詳解的詳細內(nèi)容,更多關于Feign路徑的轉義的資料請關注腳本之家其它相關文章!
相關文章
Mybatis之通用Mapper動態(tài)表名及其原理分析
這篇文章主要介紹了Mybatis之通用Mapper動態(tài)表名及其原理分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
SpringMVC接收java.util.Date類型數(shù)據(jù)的2種方式小結
這篇文章主要介紹了使用SpringMVC接收java.util.Date類型數(shù)據(jù)的2種方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
手把手教你用SpringBoot將文件打包成zip存放或導出
相信各位看官在工作中都會遇到過要把多個文件打包成一個壓縮文件然后導出,或者將文件打包成Zip存放,這就來上代碼,廢話不多說,需要的朋友可以參考下2021-06-06
java實現(xiàn)excel導入數(shù)據(jù)的工具類
這篇文章主要介紹了java實現(xiàn)的excel導入數(shù)據(jù)的工具類,需要的朋友可以參考下2014-03-03
JAVA實現(xiàn) SpringMVC方式的微信接入、實現(xiàn)簡單的自動回復功能
這篇文章主要介紹了JAVA實現(xiàn) SpringMVC方式的微信接入、實現(xiàn)簡單的自動回復功能的相關資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-11-11

