亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

feign 調(diào)用第三方服務(wù)中部分特殊符號(hào)未轉(zhuǎn)義問題

 更新時(shí)間:2022年03月07日 09:38:49   作者:0D  
這篇文章主要介紹了feign 調(diào)用第三方服務(wù)中部分特殊符號(hào)未轉(zhuǎn)義問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

調(diào)用第三方部分特殊符號(hào)未轉(zhuǎn)義

開發(fā)過(guò)程中,發(fā)現(xiàn)+(加號(hào))這個(gè)符號(hào)沒有轉(zhuǎn)義,導(dǎo)致再調(diào)用服務(wù)的時(shí)候把加號(hào)轉(zhuǎn)義成空格了。導(dǎo)致后臺(tái)獲取到的數(shù)據(jù)會(huì)不正確。

1. 問題發(fā)現(xiàn)過(guò)程

feign 解析參數(shù)的時(shí)候,使用的標(biāo)準(zhǔn)是 RFC 3986,這個(gè)標(biāo)準(zhǔn)的加號(hào)是不需要被轉(zhuǎn)義的。其具體的實(shí)現(xiàn)是 feign.template.UriUtils#encodeReserved(String value, String reserved, Charset charset)

2. 解決辦法

feign 調(diào)用過(guò)程

1. feign核心先將(定義好的feign接口)接口中的參數(shù)解析出來(lái)

2. 對(duì)接實(shí)際參數(shù)和接口參數(shù)(入?yún)⒄{(diào)用的參數(shù))

3. 對(duì)入?yún)⒌膮?shù)進(jìn)行編碼(UriUtils#encodeReserved)(問題出在這里)

4. 調(diào)用注冊(cè)的 RequestInterceptor(自定義)

5. Encoder 實(shí)現(xiàn)類,這里是body里面的內(nèi)容才會(huì)有調(diào)用(自定義)

6. 具體的http網(wǎng)絡(luò)請(qǐng)求邏輯

依據(jù)上面的過(guò)程,我們可以實(shí)現(xiàn)一個(gè) RequestInterceptor 攔截器,在這里對(duì)參數(shù)再次進(jìn)行轉(zhuǎn)義即可。

public void apply(RequestTemplate template) {
? ? Map<String, Collection<String>> _queries = template.queries();
? ? if (!_queries.isEmpty()) {
? ? ? ? //由于在最新的 ?RFC 3986 ?規(guī)范,+號(hào)是不需要編碼的,因此spring 實(shí)現(xiàn)的是這個(gè)規(guī)范,這里就需要參數(shù)中進(jìn)行編碼先,兼容舊規(guī)范。
? ? ? ? Map<String, Collection<String>> encodeQueries = new HashMap<String, Collection<String>>(_queries.size());
? ? ? ? Iterator<String> iterator = _queries.keySet().iterator();
? ? ? ? Collection<String> encodeValues = null;
? ? ? ? while (iterator.hasNext()) {
? ? ? ? ? ? encodeValues = new ArrayList<>();
? ? ? ? ? ? String key = iterator.next();
? ? ? ? ? ? Collection<String> values = _queries.get(key);
? ? ? ? ? ? for (String _str : values) {
? ? ? ? ? ? ? ? _str = _str.replaceAll("\\+", "%2B");
? ? ? ? ? ? ? ? encodeValues.add(_str);
? ? ? ? ? ? }
? ? ? ? ? ? encodeQueries.put(key, encodeValues);
? ? ? ? }
? ? ? ? template.queries(null);
? ? ? ? template.queries(encodeQueries);
? ? }
}

上面是代碼片段,詳細(xì)請(qǐng)查看 FeignRequestInterceptor.java

3. 疑問

3.1 是否可以使用 HTTPClient 的實(shí)現(xiàn)就可以解決問題?

也不行,如果不做上面的實(shí)現(xiàn),直接改用HTTPClient實(shí)現(xiàn)的話,也只是在發(fā)送的過(guò)程中起到作用,還是需要在前進(jìn)行處理。

@RequestParams & 符號(hào)未轉(zhuǎn)義

feign-core 版本

        <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-core -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>10.4.0</version>
        </dependency>

調(diào)用路徑

源碼分析

1.Template 類

package feign.template;
...
public class Template {
  protected String resolveExpression(Expression expression, Map<String, ?> variables) {
    String resolved = null;
    Object value = variables.get(expression.getName());
    // 1. 調(diào)用 SimpleExpression 的 expand() 方法
    return expression.expand(value, this.encode.isEncodingRequired());
  }
}
public final class Expressions {
  static class SimpleExpression extends Expression {
    private final FragmentType type;
    String encode(Object value) {
      // 2. 調(diào)用 UriUtils.encodeReserved() 方法,type 參數(shù)是 FragmentType.PATH_SEGMENT
      return UriUtils.encodeReserved(value.toString(), type, Util.UTF_8);
    }
    @Override
    String expand(Object variable, boolean encode) {
      StringBuilder expanded = new StringBuilder();
      expanded.append((encode) ? encode(variable) : variable);
      String result = expanded.toString();
      return result;
    }
  }
}

public class UriUtils {  
  public static String encodeReserved(String value, FragmentType type, Charset charset) {
    return encodeChunk(value, type, charset);
  }  
  private static String encodeChunk(String value, FragmentType type, Charset charset) {
    byte[] data = value.getBytes(charset);
    ByteArrayOutputStream encoded = new ByteArrayOutputStream();
    for (byte b : data) {
      if (type.isAllowed(b)) {
      // 3.1 如果不需要轉(zhuǎn)義,則不進(jìn)行轉(zhuǎn)義操作
        encoded.write(b);
      } else {
        /* percent encode the byte */
        // 3.2 否則,進(jìn)行編碼
        pctEncode(b, encoded);
      }
    }
    return new String(encoded.toByteArray());
  }
  
  enum FragmentType {
    URI {
      @Override
      boolean isAllowed(int c) {
        return isUnreserved(c);
      }
    },
    PATH_SEGMENT {
      @Override
      boolean isAllowed(int c) {
        return this.isPchar(c) || (c == '/');
      }
    }
    abstract boolean isAllowed(int c);
    protected boolean isAlpha(int c) {
      return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
    }
    protected boolean isDigit(int c) {
      return (c >= '0' && c <= '9');
    }
    protected boolean isSubDelimiter(int c) {
      return (c == '!') || (c == '$') || (c == '&') || (c == '\'') || (c == '(') || (c == ')')
          || (c == '*') || (c == '+') || (c == ',') || (c == ';') || (c == '=');
    }
    protected boolean isUnreserved(int c) {
      return this.isAlpha(c) || this.isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
    }
    protected boolean isPchar(int c) {
      return this.isUnreserved(c) || this.isSubDelimiter(c) || c == ':' || c == '@';
    }
  }
}

從源碼上可以看出,& 字符屬于 isSubDelimiter(),所以不會(huì)被轉(zhuǎn)義。

測(cè)試

package feign.template;
import feign.Util;
public class UriUtilsDemo {
? ? public static void main(String[] args) {
? ? ? ? String str = "aa&aa";
? ? ? ? // 輸出:aa&aa
? ? ? ? System.out.println(UriUtils.encodeReserved(str, UriUtils.FragmentType.PATH_SEGMENT, Util.UTF_8));
? ? ? ? // 輸出:aa%26aa
? ? ? ? System.out.println(UriUtils.encodeReserved(str, UriUtils.FragmentType.URI, Util.UTF_8));
? ? }
}

解決方案

1、升級(jí) feign-core 版本,feign-core-10.12 已經(jīng)沒有這個(gè)問題。

2、使用 @RequestBody 替換 @RequestParam。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論