激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - SpringBoot 中如何自定義參數解析器?

SpringBoot 中如何自定義參數解析器?

2021-03-17 01:03江南一點雨 JAVA教程

在一個 Web 請求中,參數我們無非就是放在地址欄或者請求體中,個別請求可能放在請求頭中。

SpringBoot 中如何自定義參數解析器?

在一個 Web 請求中,參數我們無非就是放在地址欄或者請求體中,個別請求可能放在請求頭中。

放在地址欄中,我們可以通過如下方式獲取參數:

  1. String javaboy = request.getParameter("name "); 

放在請求體中,如果是 key/value 形式,我們可以通過如下方式獲取參數:

  1. String javaboy = request.getParameter("name "); 

如果是 JSON 形式,我們則通過如果如下方式獲取到輸入流,然后解析成 JSON 字符串,再通過 JSON 工具轉為對象:

  1. BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); 
  2. String json = reader.readLine(); 
  3. reader.close(); 
  4. User user = new ObjectMapper().readValue(json, User.class); 

如果參數放在請求頭中,我們可以通過如下方式獲取:

  1. String javaboy = request.getHeader("name"); 

如果你用的是 Jsp/Servlet 那一套技術棧,那么參數獲取無外乎這幾種方式。

如果用了 SpringMVC 框架,有的小伙伴們可能會覺得參數獲取方式太豐富了,各種注解如 @RequestParam、@RequestBody、@RequestHeader、@PathVariable,參數可以是 key/value 形式,也可以是 JSON 形式,非常豐富!但是,無論多么豐富,最底層獲取參數的方式無外乎上面幾種。

那有小伙伴要問了,SpringMVC 到底是怎么樣從 request 中把參數提取出來直接給我們用的呢?例如下面這個接口:

  1. @RestController 
  2. public class HelloController { 
  3.     @GetMapping("/hello"
  4.     public String hello(String name) { 
  5.         return "hello "+name
  6.     } 

我們都知道 name 參數是從 HttpServletRequest 中提取出來的,到底是怎么提取出來的?這就是松哥今天要和大家分享的話題。

1.自定義參數解析器

為了搞清楚這個問題,我們先來自定義一個參數解析器看看。

自定義參數解析器需要實現 HandlerMethodArgumentResolver 接口,我們先來看看該接口:

  1. public interface HandlerMethodArgumentResolver { 
  2.  boolean supportsParameter(MethodParameter parameter); 
  3.  @Nullable 
  4.  Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  5.    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; 
  6.  

這個接口中就兩個方法:

  • supportsParameter:該方法表示是否啟用這個參數解析器,返回 true 表示啟用,返回 false 表示不啟用。
  • resolveArgument:這是具體的解析過程,就是從 request 中取出參數的過程,方法的返回值就對應了接口中參數的值。

自定義參數解析器只需要實現該接口即可。

假設我現在有這樣一個需求(實際上在 Spring Security 中獲取當前登錄用戶名非常方便,這里只是為了該案例而做,勿抬杠):

假設我現在系統安全框架使用了 Spring Security(對 Spring Security 不熟悉的小伙伴,可以在公眾號江南一點雨后臺回復 ss,有教程),如果我在接口的參數上添加了 @CurrentUserName 注解,那么該參數的值就是當前登錄的用戶名,像下面這樣:

  1. @RestController 
  2. public class HelloController { 
  3.     @GetMapping("/hello"
  4.     public String hello(@CurrentUserName String name) { 
  5.         return "hello "+name
  6.     } 

要實現這個功能,非常 easy,首先我們自定義一個 @CurrentUserName 注解,如下:

  1. @Retention(RetentionPolicy.RUNTIME) 
  2. @Target(ElementType.PARAMETER) 
  3. public @interface CurrentUserName { 

這個注解沒啥好解釋的。

接下來我們自定義參數解析器 CurrentUserNameHandlerMethodArgumentResolver,如下:

  1. public class CurrentUserNameHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { 
  2.     @Override 
  3.     public boolean supportsParameter(MethodParameter parameter) { 
  4.         return parameter.getParameterType().isAssignableFrom(String.class)&&parameter.hasParameterAnnotation(CurrentUserName.class); 
  5.     } 
  6.  
  7.     @Override 
  8.     public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
  9.         User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 
  10.         return user.getUsername(); 
  11.     } 

supportsParameter:如果參數類型是 String,并且參數上有 @CurrentUserName 注解,則使用該參數解析器。

resolveArgument:該方法的返回值就是參數的具體值,當前登錄用戶名從 SecurityContextHolder 中獲取即可(具體參數松哥的 Spring Security 教程,公號后臺回復 ss)。

最后,我們再將自定義的參數解析器配置到 HandlerAdapter 中,配置方式如下:

  1. @Configuration 
  2. public class WebConfig implements WebMvcConfigurer { 
  3.     @Override 
  4.     public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { 
  5.         resolvers.add(new CurrentUserNameHandlerMethodArgumentResolver()); 
  6.     } 

至此,就算配置完成了。

接下來啟動項目,用戶登錄成功后,訪問 /hello 接口,就可以看到返回當前登錄用戶數據了。

這就是我們自定義的一個參數類型解析器。可以看到,非常 Easy。

在 SpringMVC 中,默認也有很多 HandlerMethodArgumentResolver 的實現類,他們處理的問題也都類似,松哥再給大家舉個例子。

2.PrincipalMethodArgumentResolver

如果我們在項目中使用了 Spring Security,我們可以通過如下方式獲取當前登錄用戶信息:

  1. @GetMapping("/hello2"
  2. public String hello2(Principal principal) { 
  3.     return "hello " + principal.getName(); 

即直接在當前接口的參數中添加 Principal 類型的參數即可,該參數描述了當前登錄用戶信息,這個用過 Spring Security 的小伙伴應該都知道(不熟悉 Spring Security 的小伙伴可以在公眾號【江南一點雨】后臺回復 ss)。

那么這個功能是怎么實現的呢?當然就是 PrincipalMethodArgumentResolver 在起作用了!

我們一起來看下這個參數解析器:

  1. public class PrincipalMethodArgumentResolver implements HandlerMethodArgumentResolver { 
  2.  
  3.  @Override 
  4.  public boolean supportsParameter(MethodParameter parameter) { 
  5.   return Principal.class.isAssignableFrom(parameter.getParameterType()); 
  6.  } 
  7.  
  8.  @Override 
  9.  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  10.    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { 
  11.  
  12.   HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 
  13.   if (request == null) { 
  14.    throw new IllegalStateException("Current request is not of type HttpServletRequest: " + webRequest); 
  15.   } 
  16.  
  17.   Principal principal = request.getUserPrincipal(); 
  18.   if (principal != null && !parameter.getParameterType().isInstance(principal)) { 
  19.    throw new IllegalStateException("Current user principal is not of type [" + 
  20.      parameter.getParameterType().getName() + "]: " + principal); 
  21.   } 
  22.  
  23.   return principal; 
  24.  } 
  25.  

supportsParameter:這個方法主要是判斷參數類型是不是 Principal,如果參數類型是 Principal,就支持。

resolveArgument:這個方法的邏輯很簡單,首先獲取原生的請求,再從請求中獲取 Principal 對象返回即可。

是不是很簡單,有了這個,我們就可以隨時加載到當前登錄用戶信息了。

3.RequestParamMapMethodArgumentResolver

松哥再給大家舉個例子:

  1. @RestController 
  2. public class HelloController { 
  3.     @PostMapping("/hello"
  4.     public void hello(@RequestParam MultiValueMap map) throws IOException { 
  5.         //省略... 
  6.     } 

這個接口很多小伙伴可能都寫過,使用 Map 去接收前端傳來的參數,那么這里用到的參數解析器就是 RequestParamMapMethodArgumentResolver。

  1. public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver { 
  2.  
  3.  @Override 
  4.  public boolean supportsParameter(MethodParameter parameter) { 
  5.   RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); 
  6.   return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && 
  7.     !StringUtils.hasText(requestParam.name())); 
  8.  } 
  9.  
  10.  @Override 
  11.  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  12.    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { 
  13.  
  14.   ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); 
  15.  
  16.   if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) { 
  17.    // MultiValueMap 
  18.    Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve(); 
  19.    if (valueType == MultipartFile.class) { 
  20.     MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); 
  21.     return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0)); 
  22.    } 
  23.    else if (valueType == Part.class) { 
  24.     HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); 
  25.     if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { 
  26.      Collection<Part> parts = servletRequest.getParts(); 
  27.      LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size()); 
  28.      for (Part part : parts) { 
  29.       result.add(part.getName(), part); 
  30.      } 
  31.      return result; 
  32.     } 
  33.     return new LinkedMultiValueMap<>(0); 
  34.    } 
  35.    else { 
  36.     Map<String, String[]> parameterMap = webRequest.getParameterMap(); 
  37.     MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size()); 
  38.     parameterMap.forEach((keyvalues) -> { 
  39.      for (String value : values) { 
  40.       result.add(key, value); 
  41.      } 
  42.     }); 
  43.     return result; 
  44.    } 
  45.   } 
  46.  
  47.   else { 
  48.    // Regular Map 
  49.    Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve(); 
  50.    if (valueType == MultipartFile.class) { 
  51.     MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); 
  52.     return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0)); 
  53.    } 
  54.    else if (valueType == Part.class) { 
  55.     HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); 
  56.     if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { 
  57.      Collection<Part> parts = servletRequest.getParts(); 
  58.      LinkedHashMap<String, Part> result = CollectionUtils.newLinkedHashMap(parts.size()); 
  59.      for (Part part : parts) { 
  60.       if (!result.containsKey(part.getName())) { 
  61.        result.put(part.getName(), part); 
  62.       } 
  63.      } 
  64.      return result; 
  65.     } 
  66.     return new LinkedHashMap<>(0); 
  67.    } 
  68.    else { 
  69.     Map<String, String[]> parameterMap = webRequest.getParameterMap(); 
  70.     Map<String, String> result = CollectionUtils.newLinkedHashMap(parameterMap.size()); 
  71.     parameterMap.forEach((keyvalues) -> { 
  72.      if (values.length > 0) { 
  73.       result.put(keyvalues[0]); 
  74.      } 
  75.     }); 
  76.     return result; 
  77.    } 
  78.   } 
  79.  } 
  80.  

supportsParameter:參數類型是 Map,并且使用了 @RequestParam 注解,并且 @RequestParam 注解中沒有配置 name 屬性,就可以使用該參數解析器。

resolveArgument:具體解析分為兩種情況:MultiValueMap 和其他 Map,前者中又分三種情況:MultipartFile、Part 或者其他普通請求,前兩者可以處理文件上傳,第三個就是普通參數。如果是普通 Map,則直接獲取到原始請求參數放到一個 Map 集合中返回即可。

4.小結

前面和大家聊的都是幾種簡單的情況,還有復雜的如 PathVariableMethodArgumentResolver 和 RequestParamMethodArgumentResolver 松哥以后再和大家詳細聊。同時還有一個問題就是這些參數解析器具體是在哪里調用的,這個也會在松哥近期的 SpringMVC 源碼解析系列文章中和大家分享,好啦,今天周末,就這點簡單的小知識祝大家周末愉快~

原文地址:https://mp.weixin.qq.com/s/4c-uV8f6x5UPvxec6Em79A

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 中国杭州少妇xxxx做受 | 成人午夜高清 | 国产精品av久久久久久久久久 | 在线观看视频毛片 | 久久久久久久久久综合 | 亚洲va国产va| 国产日产精品一区二区三区四区 | 免费在线观看成年人视频 | 成品片a免费直接观看 | 成年人在线视频观看 | 天天干导航 | 日本在线免费观看视频 | 黄色一级片在线观看 | 亚洲午夜不卡 | 日日噜噜夜夜爽 | 奇米影视奇米色777欧美 | 国产精品久久久久久婷婷天堂 | 媚药按摩痉挛w中文字幕 | 日韩美香港a一级毛片 | av国产免费| 性欧美xxxx免费岛国不卡电影 | 青草视频在线观看视频 | 日本在线不卡一区二区 | 久色成人| 日韩黄色免费观看 | 久久久久久久亚洲视频 | 国产美女一区二区在线观看 | 欧美高清在线精品一区二区不卡 | www.99久| av手机在线电影 | 亚洲草原天堂 | 97人操 | 一级性生活免费视频 | 在线观看精品视频 | 亚洲成人欧美 | 中国7777高潮网站 | 一区在线免费视频 | 99最新网址 | 爱爱视频天天干 | 13一14毛片免费看 | 一区二区三区在线播放视频 |