獲取Spring中@PathVariable註解裡帶點的完整參數

来源:https://www.cnblogs.com/eaglelihh/archive/2022/05/15/16273654.html
-Advertisement-
Play Games

通過URL的尾碼發現了Spring中的useSuffixPatternMatch這個參數 ...


背景

spring-boot的版本是2.1.4.RELEASE,spring的版本是5.1.6.RELEASE

一個例子如下:

@Configuration
@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)
@SuppressWarnings("unchecked")
public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new RequestMappingHandlerMapping();
    }
}

@RestController
public class ParamController {
    @GetMapping(value = "/param/{param1}")
    public String param(@PathVariable("param1") String param1) {
        return param1;
    }
}

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

啟動一下,訪問http://127.0.0.1:8080/param/hehehttp://127.0.0.1:8080/param/hehe.hehe都返回hehe

如果訪問http://127.0.0.1:8080/param/hehe.hehe.hehe,它會返回hehe.hehe

所以會發現它把最後一個小數點後面的字元給截掉了,那如果我們想要獲取完整的字元串,該怎麼辦呢?

探索

  1. 參數怎麼來的

入口在InvocableHandlerMethod.invokeForRequest,如下:

是根據PathVariableMethodArgumentResolver.resolveName得來的,如下:

HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

什麼時候放進attributes里的?,在RequestMappingInfoHandlerMapping.handleMatch,如下:

  1. 參數怎麼解析的
程式定義的:/param/{param1} -> /param/{param1}.*
介面傳過來的:/param/hehe.hehe

以/分割,第一個字元串param里沒有參數,所以會跳過,直接看第二個字元串:
{param1}.* -> pattern=(.*)\Q.\E.*
hehe.hehe

param1=hehe

我們定義的是/param/{param1},怎麼就變成了/param/{param1}.*?在PatternsRequestCondition.getMatchingPattern

可以看到如果useSuffixPatternMatch為true,並且指定的url里沒有.,會在尾碼自動增加.*

  1. useSuffixPatternMatch是在哪裡設置的?
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
		implements MatchableHandlerMapping, EmbeddedValueResolverAware {

	private boolean useSuffixPatternMatch = true;

	@Override
	public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); // 這裡設置了
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());

		super.afterPropertiesSet();
	}
}

解決

  1. 修改WebConfiggetRequestMappingHandlerMapping
@Configuration
@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)
@SuppressWarnings("unchecked")
public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
        return requestMappingHandlerMapping;
    }
}
  1. 增加configurePathMatch方法
@Configuration
@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)
@SuppressWarnings("unchecked")
public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new RequestMappingHandlerMapping();
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(false);
    }
}
  1. url中增加點

a. 中間的參數是不受影響的

@RestController
public class ParamController {
    @GetMapping(value = "/param/{param1}/{param2}")
    public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {
        return param1 + " " + param2;
    }
}

訪問http://127.0.0.1:8080/param/hehe.hehe/hehe.hee返回hehe.hehe hehe

b. 增加點

@RestController
public class ParamController {
    //@GetMapping(value = "/param/{param1}")
    //public String param(@PathVariable("param1") String param1) {
    //    return param1;
    //}

    @GetMapping(value = "/param/{param1}.{param2}")
    public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {
        return param1 + " " + param2;
    }
}

訪問http://127.0.0.1:8080/param/hehe.hehe返回hehe hehe

註意第一個方法和第二個方法不要同時出現,如果同時出現的話,則會訪問第一個方法

@RestController
public class ParamController {
    @GetMapping(value = "/param/{param1}")
    public String param(@PathVariable("param1") String param1) {
        return param1;
    }

    @GetMapping(value = "/param/{param1}.{param2}")
    public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {
        return param1 + " " + param2;
    }
}

訪問http://127.0.0.1:8080/param/hehe.heh返回hehe

  1. 修改參數
@RestController
public class ParamController {
    @GetMapping(value = "/param/{param1:.+}")
    public String param(@PathVariable("param1") String param1) {
        return param1;
    }
}

訪問http://127.0.0.1:8080/param/hehe.hehe,返回hehe.hehe

原因如下:

/param/{param1:.+} -> /param/{param1:.+}
/param/hehe.hehe

{param1:.+} -> pattern=(.+)
hehe.hehe

param1=hehe.hehe
得到pattern以及提取參數的類 AntPathStringMatcher
	protected static class AntPathStringMatcher {

		private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");

		private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";

		private final Pattern pattern;

		private final List<String> variableNames = new LinkedList<>();

		public AntPathStringMatcher(String pattern) {
			this(pattern, true);
		}

		public AntPathStringMatcher(String pattern, boolean caseSensitive) {
			StringBuilder patternBuilder = new StringBuilder();
			Matcher matcher = GLOB_PATTERN.matcher(pattern);
			int end = 0;
			while (matcher.find()) {
				patternBuilder.append(quote(pattern, end, matcher.start()));
				String match = matcher.group();
				if ("?".equals(match)) {
					patternBuilder.append('.');
				}
				else if ("*".equals(match)) {
					patternBuilder.append(".*");
				}
				else if (match.startsWith("{") && match.endsWith("}")) {
					int colonIdx = match.indexOf(':');
					if (colonIdx == -1) {
						patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
						this.variableNames.add(matcher.group(1));
					}
					else {
						String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
						patternBuilder.append('(');
						patternBuilder.append(variablePattern);
						patternBuilder.append(')');
						String variableName = match.substring(1, colonIdx);
						this.variableNames.add(variableName);
					}
				}
				end = matcher.end();
			}
			patternBuilder.append(quote(pattern, end, pattern.length()));
			this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
					Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
		}

		private String quote(String s, int start, int end) {
			if (start == end) {
				return "";
			}
			return Pattern.quote(s.substring(start, end));
		}

		/**
		 * Main entry point.
		 * @return {@code true} if the string matches against the pattern, or {@code false} otherwise.
		 */
		public boolean matchStrings(String str, @Nullable Map<String, String> uriTemplateVariables) {
			Matcher matcher = this.pattern.matcher(str);
			if (matcher.matches()) {
				if (uriTemplateVariables != null) {
					// SPR-8455
					if (this.variableNames.size() != matcher.groupCount()) {
						throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
								this.pattern + " does not match the number of URI template variables it defines, " +
								"which can occur if capturing groups are used in a URI template regex. " +
								"Use non-capturing groups instead.");
					}
					for (int i = 1; i <= matcher.groupCount(); i++) {
						String name = this.variableNames.get(i - 1);
						String value = matcher.group(i);
						uriTemplateVariables.put(name, value);
					}
				}
				return true;
			}
			else {
				return false;
			}
		}
	}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 華為分析服務面向開發者提供兩種數據展現方式:(1)事件數據下載,開發者可以將事件數據下載後導入到自有的分析系統中;(2)直接在AGC概覽頁面查看用戶數和事件數。 問題描述 某開發者想將事件數據導入到自有系統中,他在導出數據時將標識用戶方式選擇“按UserID和設備”,最後發現導出數據中的事件數比分析 ...
  • 不通過路由的情況下, 懶載入一個angular模塊, 並動態創建其中聲明的組件 ...
  • 忙裡偷閑,還在學校,趁機把後面的路由多出來的知識點學完 十.緩存路由組件 讓不展示的路由組件保持掛載,不被銷毀 在我們的前面案例有一個問題,都知道vue的路由當我們切換一個路由後,另一個路由就會被銷毀,如果我在一個路由寫了有一些input框 當我切換到另一個組件message很明顯這個時候news里 ...
  • Vue-mini 完整的Demo示例:[email protected]:xsk-walter/Vue-mini.git 一、Vue實例 構造函數: $option\ $el\ $data 判斷是否存在 通過 || 邏輯運算符; _ProxyData 遍歷所有data屬性,並註入到vue實例中; 判斷是否 ...
  • 數組對象 創建數組 創建方式1: var arrname = [元素0,元素1,….]; // var arr=[1,2,3]; 創建方式2: var arrname = new Array(元素0,元素1,….); // var test=new Array(100,"a",true); 數組方法 ...
  • Eclipse WindowBuilder(SWT)插件安裝及初次使用(萌新) 一、插件安裝 (有VPN的掛VPN,伺服器在外網更新下載比較慢) 1.首先更新到最新版本 點擊Help,點擊check for update,右下角顯示查詢進度,查詢完畢會顯示最新版內容。全部更新 2.還是在help內, ...
  • 時間真的好快呀!一晃眼五一假期就結束了,假期大家都玩的開心不?今天我要給大家分享幾個練手 的基礎案例,保證大家都沒有玩過。話不多說,來來來… 一、超市購買薯片 python學習交流Q群:906715085##### # 用戶輸入薯片的單價 danjia = float(input("薯片的單價")) ...
  • 這節課是巡安似海PyHacker編寫指南的《Sql註入腳本編寫》有些註入點sqlmap跑不出,例如延時註入,實際延時與語句延時時間不符,sqlmap就跑不出,這就需要我們自己根據實際情況編寫腳本來註入了。文末,涉及了sqlmap tamper編寫,所以需要一定的python基礎才能看懂。喜歡用Pyt... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...