<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • Feign通過自定義注解實現路徑的轉義

    在這里插入圖片描述

    本文主要講解如果通過注解實現對路由中的路徑進行自定義編碼

    背景

    近期由于項目中需要,所以需要通過Feign封裝一個對Harbor操作的sdk信息。
    在調用的過程中發現,當請求參數中帶有"/"時,Feign默認會將"/"當成路徑去解析,而不是當成完整的一個參數解析,實例如下
    請求路徑為:api/v2.0/projects/{projectName}/repositories
    注解參數為:@PathVariable("projectName")
    正常請求為:api/v2.0/projects/test/repositories
    異常路徑為:api/v2.0/projects/test/pro/repositories
    相信細心的同學已經發現上面的差異了,正常的{projectName}中對應的值為test,而異常的卻對應為test/pro,所以當異常的請求打到harbor的機器時,被解析為api/v2.0/projects/test/pro/repositories,所以會直接返回404
    以上就是背景了,所以接下來我們討論一下解決方案

    解決方案

    首先我們知道springboot中默認是帶有幾種注釋參數處理器的

    @MatrixVariableParameterProcessor
    @PathVariableParameterProcessor
    @QueryMapParameterProcessor
    @RequestHeaderParameterProcessor
    @RequestParamParameterProcessor
    @RequestPartParameterProcessor
    

    因為我們的請求參數是在路徑中的,所以默認我們會使用@PathVariableParameterProcessor來標識路徑參數,而我們需要轉義的參數其實也是在路徑中,所以我們先來看一下@PathVariableParameterProcessor是如何實現的

    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注解的參數,然后再將其添加到fromParams中就可以。
    看到這里我們是不是可以想到,既然在這里我們可以拿到對應的參數了,那想做什么事情不都是由我們自己來決定了,接下來說干就干,
    首先我們聲明一個屬于自己的注解,

    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;
    }
    

    聲明完注解后,我們就需要來自定義自己的參數解析器了,首先繼承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;
        }
    }
    

    可以看到上面的代碼,我們獲取到自定義注解的參數后,將當前參數添加打Param后,并且為當前參數指定自定義的編碼格式。
    最后,我們再通過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);
        }
    }
    

    最后我們將上面的參數注解PathVariable換成我們自定義的@SlashPathVariable,就大功告成了

    最后

    通過以上的形式進行注入的話,會注入到Spring全局,所以在使用的過程中需要考慮是否符合場景

    如有哪里講得不是很明白或是有錯誤,歡迎指正
    如您喜歡的話不妨點個贊收藏一下吧??

    posted @ 2022-06-27 20:22  初夏的陽光丶  閱讀(57)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看