中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBoot 中怎么自定義參數解析器

發布時間:2021-07-08 16:52:23 來源:億速云 閱讀:184 作者:Leah 欄目:開發技術

今天就跟大家聊聊有關SpringBoot 中怎么自定義參數解析器,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

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

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

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

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

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

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

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

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

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

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

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

@RestController public class HelloController {     @GetMapping("/hello")     public String hello(String name) {         return "hello "+name;     } }

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

1.自定義參數解析器

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

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

public interface HandlerMethodArgumentResolver {  boolean supportsParameter(MethodParameter parameter);  @Nullable  Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;  }

這個接口中就兩個方法:

  • supportsParameter:該方法表示是否啟用這個參數解析器,返回 true 表示啟用,返回 false 表示不啟用。

  • resolveArgument:這是具體的解析過程,就是從 request 中取出參數的過程,方法的返回值就對應了接口中參數的值。

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

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

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

@RestController public class HelloController {     @GetMapping("/hello")     public String hello(@CurrentUserName String name) {         return "hello "+name;     } }

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

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface CurrentUserName { }

這個注解沒啥好解釋的。

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

public class CurrentUserNameHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {     @Override     public boolean supportsParameter(MethodParameter parameter) {         return parameter.getParameterType().isAssignableFrom(String.class)&&parameter.hasParameterAnnotation(CurrentUserName.class);     }      @Override     public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {         User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();         return user.getUsername();     } }

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

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

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

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

至此,就算配置完成了。

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

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

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

2.PrincipalMethodArgumentResolver

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

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

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

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

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

public class PrincipalMethodArgumentResolver implements HandlerMethodArgumentResolver {   @Override  public boolean supportsParameter(MethodParameter parameter) {   return Principal.class.isAssignableFrom(parameter.getParameterType());  }   @Override  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);   if (request == null) {    throw new IllegalStateException("Current request is not of type HttpServletRequest: " + webRequest);   }    Principal principal = request.getUserPrincipal();   if (principal != null && !parameter.getParameterType().isInstance(principal)) {    throw new IllegalStateException("Current user principal is not of type [" +      parameter.getParameterType().getName() + "]: " + principal);   }    return principal;  }  }

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

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

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

3.RequestParamMapMethodArgumentResolver

松哥再給大家舉個例子:

@RestController public class HelloController {     @PostMapping("/hello")     public void hello(@RequestParam MultiValueMap map) throws IOException {         //省略...     } }

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

public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {   @Override  public boolean supportsParameter(MethodParameter parameter) {   RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);   return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&     !StringUtils.hasText(requestParam.name()));  }   @Override  public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {    ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);    if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {    // MultiValueMap    Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();    if (valueType == MultipartFile.class) {     MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);     return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));    }    else if (valueType == Part.class) {     HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);     if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {      Collection<Part> parts = servletRequest.getParts();      LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size());      for (Part part : parts) {       result.add(part.getName(), part);      }      return result;     }     return new LinkedMultiValueMap<>(0);    }    else {     Map<String, String[]> parameterMap = webRequest.getParameterMap();     MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());     parameterMap.forEach((key, values) -> {      for (String value : values) {       result.add(key, value);      }     });     return result;    }   }    else {    // Regular Map    Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve();    if (valueType == MultipartFile.class) {     MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);     return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));    }    else if (valueType == Part.class) {     HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);     if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {      Collection<Part> parts = servletRequest.getParts();      LinkedHashMap<String, Part> result = CollectionUtils.newLinkedHashMap(parts.size());      for (Part part : parts) {       if (!result.containsKey(part.getName())) {        result.put(part.getName(), part);       }      }      return result;     }     return new LinkedHashMap<>(0);    }    else {     Map<String, String[]> parameterMap = webRequest.getParameterMap();     Map<String, String> result = CollectionUtils.newLinkedHashMap(parameterMap.size());     parameterMap.forEach((key, values) -> {      if (values.length > 0) {       result.put(key, values[0]);      }     });     return result;    }   }  }  }

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

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

看完上述內容,你們對SpringBoot 中怎么自定義參數解析器有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

营山县| 惠州市| 买车| 宜丰县| 蓬安县| 武乡县| 青阳县| 聂拉木县| 会泽县| 凉城县| 错那县| 兴文县| 宝坻区| 新建县| 阳朔县| 达尔| 太湖县| 临高县| 射洪县| 修水县| 汤原县| 大城县| 南投县| 怀柔区| 武隆县| 宣化县| 清流县| 高台县| 邢台县| 乃东县| 齐齐哈尔市| 鄂尔多斯市| 灵台县| 山丹县| 仙桃市| 通州市| 绥芬河市| 板桥市| 远安县| 和田市| 克东县|