欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何處理@PathVariable中的特殊字符問題

 更新時間:2021年02月25日 11:52:39   作者:liang家之言  
這篇文章主要介紹了如何處理@PathVariable中的特殊字符問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

上代碼:

 @GetMapping(value="/user/{useraccount}")
 public void getUserAccount(@PathVariable("useraccount") String userAccount) {
 
 logger.info("useraccount :" + userAccount);
 }

正常訪問:

/user/zhangsan

打?。簎seraccount : zhangsan

看似一切正常

but:

訪問:/user/zhangsan/lisi

打印:useraccount : zhangsan

咦,為啥不是useraccount :zhangsan/lisi ?

@PathVariable并沒有我們想象的聰明,對于參數(shù)中的/并不能跟實際路徑/分開

事實上,有. ; -等都不能正確切分。

怎么辦呢?

兩種方案:

1,簡單點,直接使用@RequestParam代替

 @GetMapping(value="/user")
 public void getUserAccount(@RequestParam("useraccount") String userAccount) {
 
 logger.info("useraccount :" + userAccount);
 }

用/user?useraccount=zhangsan 訪問

2,使用正則過濾

 @GetMapping(value="/user/{useraccount:[a-zA-Z0-9\\.\\-\\_\\;\\\]+}")
 public void getUserAccount(@PathVariable("useraccount") String userAccount) {
 
 logger.info("useraccount :" + userAccount);
 }

正常訪問:

/user/zhangsan

打?。簎seraccount : zhangsan

當(dāng)然,這個就有點不靈活了,第一種簡單又方便

補充:記一次@PathVariable特殊參數(shù)會丟失的排查問題

請求參數(shù)中如果包含.,會造成參數(shù)丟失,請看如下代碼

以下代碼,省略@RestController控制層類代碼

@RequestMapping(value = "hello/{name}")
public Map<String, Object> sayHello(@PathVariable("name") String name, HttpServletRequest request) {
 Map<String, Object> rtnMap = new HashMap<>();
 rtnMap.put("msg", "hello " + name);
 return rtnMap;
}

請求地址: hello/ddf,則正常返回{"msg": "hello ddf"}

請求地址: hello/ddf.com,依然還是返回{"msg": "hello ddf"}

如果需要解決上面這個問題,則可以將代碼更改如下(該解決方式從網(wǎng)上搜尋)

@RequestMapping(value = "hello/{name:.*}")
public Map<String, Object> sayHello(@PathVariable("name") String name, HttpServletRequest request) {
 Map<String, Object> rtnMap = new HashMap<>();
 rtnMap.put("msg", "hello " + name);
 return rtnMap;
}

如果使用@PathVariable以.sh或.bat等特殊字符結(jié)尾,會影響實際返回數(shù)據(jù)

報錯如下:

{
 "timestamp": 1541405292119,
 "status": 406,
 "error": "Not Acceptable",
 "exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
 "message": "Could not find acceptable representation",
 "path": "/HDOrg/user/hello/ddf.sh"
}

還是上面的代碼

以下代碼,省略@RestController控制層類代碼

@RequestMapping(value = "hello/{name:.*}")
public Map<String, Object> sayHello(@PathVariable("name") String name, HttpServletRequest request) {
 Map<String, Object> rtnMap = new HashMap<>();
 rtnMap.put("msg", "hello " + name);
 return rtnMap;
}

如果這時候請求地址為hello/ddf.sh或hello/ddf.com.sh,只要是以.sh結(jié)尾,這時候業(yè)務(wù)邏輯代碼不會受到影響,但走到Spring自己的代碼去處理返回數(shù)據(jù)的時候,有一個功能會根據(jù)擴展名來決定返回的類型,而以.sh結(jié)尾擴展名為sh,會被解析成對應(yīng)的Content-Type: application/x-sh。

解決辦法如下,第一種方法是從網(wǎng)上找到的,可以直接禁用該功能,但有可能會影響到靜態(tài)資源的訪問,不能確定,也沒有進行嘗試

@Configuration
public class Config extends WebMvcConfigurerAdapter {
 @Override
 public void configureContentNegotiation(
  ContentNegotiationConfigurer configurer) {
 configurer.favorPathExtension(false);
 }
}

然后以下就是閑著沒事很想換個思路嘗試去看看這到底是怎么回事,由于個人能力有限,不保證以下內(nèi)容的重要性;

第二種方式解決思路是,既然擴展名以.sh等結(jié)尾會有問題,那么能不能不要讓程序?qū)U展名識別為.sh,或者干脆就跳過處理,比如我是否可以加個.sh/這樣就會影響到實際的擴展名,但是又不會影響到已有的代碼,其實這里有個偷懶的寫法,可以直接在@RequestMapping里的value最后直接加一個/,但是這要求客戶端必須在原有的條件上最終拼一個/,否則會找不到對應(yīng)的映射,直接404,我這里碰到這個問題的時候,因為該方法已經(jīng)上線并且被其它幾個系統(tǒng)調(diào)用,因此更改起來會有些繁瑣,所以尋求了一種麻煩的方式,先將解決方式放在下面,不確定是否會影響其它問題

這種方式解決方式如下:注釋中的兩行代碼二選一都可,推薦前面的寫法,直接已經(jīng)跳過

@RequestMapping(value = "hello/{name:.*}")
public String sayHello(@PathVariable("name") String name) {
 // 該方法跳過通過上面描述的那種方式來確定MediaType
 request.setAttribute(PathExtensionContentNegotiationStrategy.class.getName() + ".SKIP", true);
 // 后面參數(shù)的值前半部分必須和該方法的RequestMapping一致,否則無效,不包括ContextPath
 request.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/hello/" + name + "/");
 return "hello " + name;
}

下面依賴源碼來看一下為什么可以這么去做,先看一下為什么會造成這個結(jié)果?以下步驟只關(guān)心與當(dāng)前問題有關(guān)的部分,并只大概關(guān)注其中問題,不作細(xì)節(jié)的深入

經(jīng)過debug可以看到錯誤是在處理以下過程報錯,首先如下

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
 @Override
 public void handleReturnValue(Object returnValue, MethodParameter returnType,
  ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
  throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
 mavContainer.setRequestHandled(true);
 ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
 ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
 // Try even with null return value. ResponseBodyAdvice could get involved.
 writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
 }
}

出現(xiàn)這個問題,一般的查找思路就是是否是請求或響應(yīng)的Content-Type是否出現(xiàn)了問題,那么在上面這個方法上無論是inputMessage還是outputMessage都是正常的,重點來看一下writeWithMessageConverters()方法,該方法,部分代碼如下

public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
 implements HandlerMethodReturnValueHandler {
 @SuppressWarnings("unchecked")
 protected <T> void writeWithMessageConverters(T value, MethodParameter returnType,
       ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
 throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
 Object outputValue;
 Class<?> valueType;
 Type declaredType;
 if (value instanceof CharSequence) {
  outputValue = value.toString();
  valueType = String.class;
  declaredType = String.class;
 }
 else {
  outputValue = value;
  valueType = getReturnValueType(outputValue, returnType);
  declaredType = getGenericType(returnType);
 }
 HttpServletRequest request = inputMessage.getServletRequest();
 List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
 List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
 // 后面處理MediaType的部分在這里全部省略
 } 
 
 /**
 * Returns the media types that can be produced:
 * <ul>
 * <li>The producible media types specified in the request mappings, or
 * <li>Media types of configured converters that can write the specific return value, or
 * <li>{@link MediaType#ALL}
 * </ul>
 * @since 4.2
 */
 protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, Type declaredType) {
 Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
 if (!CollectionUtils.isEmpty(mediaTypes)) {
  return new ArrayList<MediaType>(mediaTypes);
 }
 else if (!this.allSupportedMediaTypes.isEmpty()) {
  List<MediaType> result = new ArrayList<MediaType>();
  for (HttpMessageConverter<?> converter : this.messageConverters) {
  if (converter instanceof GenericHttpMessageConverter && declaredType != null) {
   if (((GenericHttpMessageConverter<?>) converter).canWrite(declaredType, valueClass, null)) {
   result.addAll(converter.getSupportedMediaTypes());
   }
  }
  else if (converter.canWrite(valueClass, null)) {
   result.addAll(converter.getSupportedMediaTypes());
  }
  }
  return result;
 }
 else {
  return Collections.singletonList(MediaType.ALL);
 }
 }
}

先看方法getAcceptableMediaTypes(),是根據(jù)請求來決定當(dāng)前的HttpServletRequest到底是要請求什么類型的數(shù)據(jù),該方法調(diào)用鏈在后面說明;

getProducibleMediaTypes()方法返回可以生成的MediaType,能夠生成哪些是看當(dāng)前項目一共有多少可以被支持的MediaType,當(dāng)然也能看到也可以通過HttpServletRequest明確設(shè)置屬性HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE來確定用哪種方式;

拿到這兩個列表后,需要判斷requestedMediaTypes是否兼容producibleMediaTypes,如*/*則可以兼容所有的可以生成的MediaType,最終將兼容的requestedMediaTypes循環(huán)處理,看是否是一個具體的MediaType而不是通配符,那么最終生效的MediaType就是這個,當(dāng)然存在多個,則也就存在多個不是通配也滿足條件的,所以再循環(huán)前也做了一次排序,保證優(yōu)先級最高的一定會生效。

public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
 implements HandlerMethodReturnValueHandler { 
 private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request) throws HttpMediaTypeNotAcceptableException {
 List<MediaType> mediaTypes = this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
 return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes);
 } 
}

MediaType.java

public class MediaType extends MimeType implements Serializable {
 public static final MediaType ALL;
 /**
 * A String equivalent of {@link MediaType#ALL}.
 */
 public static final String ALL_VALUE = "*/*";
 
 // 靜態(tài)初始化MediaType.ALL的值省略
}

該方法的結(jié)果可以看到如果調(diào)用的方法返回了一個空的列表,則該方法返回MediaType.ALL的列表,通過代碼可以看到它的值為*/*,該方法往下調(diào)用部分代碼如下:

public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
 
 @Override
 public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
 for (ContentNegotiationStrategy strategy : this.strategies) {
  List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
  if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
  continue;
  }
  return mediaTypes;
 }
 return Collections.emptyList();
 }
}

調(diào)用如下:

public class WebMvcAutoConfiguration {
 @Override
 public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
 throws HttpMediaTypeNotAcceptableException {
 
 private static final String SKIP_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class
  .getName() + ".SKIP";
 
 Object skip = webRequest.getAttribute(SKIP_ATTRIBUTE,
      RequestAttributes.SCOPE_REQUEST);
 if (skip != null && Boolean.parseBoolean(skip.toString())) {
  return Collections.emptyList();
 }
 return this.delegate.resolveMediaTypes(webRequest);
 }
}

在這里可以看到有一個屬性為skip,如果它的屬性為PathExtensionContentNegotiationStrategy的類全名+".SKP"并且它的值為true,那么這里則不繼續(xù)往下處理直接返回空的集合,而在前面也已經(jīng)看到如果返回的空的集合,實際上最終返回給調(diào)用方的是*/*,結(jié)合前面看到的

org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)

這個方法,*/*是可以匹配任何生成的producibleMediaTypes,所以最終結(jié)果能夠按照原先應(yīng)該返回的類型正確返回,而不會被.sh等后綴影響到;

其實最初沒有看到skip的時候,看到了一些后面的代碼,最終也解決了這個問題,不論正確與否,先把整個過程記錄下來,假如在上面的步驟中沒有設(shè)置skip=true,那么程序繼續(xù)下去的部分走向如下

如果uid以.sh結(jié)尾的話,在邏輯處理完成之后框架處理return數(shù)據(jù)的時候,會根據(jù)擴展名來決定返回的content-type,sh結(jié)尾

會影響返回的content-type為application/x-sh,這會影響該方法的實際功能,解決辦法是:

要么禁用該功能,要么修改該方法的@RequestMapping,禁用不能確定是否會對直接訪問的靜態(tài)資源有影響,

而且該方法調(diào)用方項目已上線,不宜輕易修改,只能這里改變這個屬性的地址,影響框架

后面獲取請求的后綴為null,而避免這個問題,但尚不能確認(rèn)requestUrl和mappingUrl不一致是否會有別的問題

request.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/user/" + uid + "/");

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • Java 合并多個MP4視頻文件

    Java 合并多個MP4視頻文件

    這篇文章主要介紹了Java 合并多個MP4視頻文件的方法,幫助大家利用Java處理視頻,提高辦公效率,感興趣的朋友可以了解下
    2020-11-11
  • SpringBoot+WebSocket向前端推送消息的實現(xiàn)示例

    SpringBoot+WebSocket向前端推送消息的實現(xiàn)示例

    WebSocket是一種在單個TCP連接上進行全雙工通信的協(xié)議,允許服務(wù)器主動向客戶端推送信息,同時也能從客戶端接收信息,本文主要介紹了SpringBoot+WebSocket向前端推送消息的實現(xiàn)示例,感興趣的可以了解一下
    2024-08-08
  • Java編程求二叉樹的鏡像兩種方法介紹

    Java編程求二叉樹的鏡像兩種方法介紹

    這篇文章主要介紹了Java編程求二叉樹的鏡像兩種方法介紹,分享了兩種方法,遞歸與非遞歸,每種方法又分別介紹了兩種解決思路,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 簡述Java圖像傾斜方法及實例

    簡述Java圖像傾斜方法及實例

    這篇文章主要介紹了Java圖像傾斜的方法及實例,需要的朋友可以參考下
    2017-09-09
  • Java結(jié)合JS實現(xiàn)URL編碼與解碼

    Java結(jié)合JS實現(xiàn)URL編碼與解碼

    這篇文章介紹了Java結(jié)合JS實現(xiàn)URL編碼與解碼的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • SpringBoot獲取前臺參數(shù)的六種方式以及統(tǒng)一響應(yīng)

    SpringBoot獲取前臺參數(shù)的六種方式以及統(tǒng)一響應(yīng)

    本文主要介紹了SpringBoot獲取前臺參數(shù)的六種方式以及統(tǒng)一響應(yīng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • java 內(nèi)嵌Groovy動態(tài)腳本操作

    java 內(nèi)嵌Groovy動態(tài)腳本操作

    這篇文章主要介紹了java 內(nèi)嵌Groovy動態(tài)腳本操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • restTemplate實現(xiàn)跨服務(wù)API調(diào)用方式

    restTemplate實現(xiàn)跨服務(wù)API調(diào)用方式

    這篇文章主要介紹了restTemplate實現(xiàn)跨服務(wù)API調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。
    2023-07-07
  • Spring中property-placeholder的使用與解析詳解

    Spring中property-placeholder的使用與解析詳解

    本篇文章主要介紹了Spring中property-placeholder的使用與解析詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • Spring boot集成RabbitMQ的示例代碼

    Spring boot集成RabbitMQ的示例代碼

    本篇文章主要介紹了Spring boot集成RabbitMQ的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05

最新評論