最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解決    Spring RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)問題

 更新時(shí)間:2020年02月07日 14:05:04   作者:漫夭  
本文詳解說(shuō)明了RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)的問題及其原由,需要的朋友可以參考下

今天跟同事接口聯(lián)調(diào),使用RestTemplate請(qǐng)求服務(wù)端的post接口(使用python開發(fā))。詭異的是,post請(qǐng)求,返回500 Internal Server Error,而使用get請(qǐng)求,返回正常。代碼如下:

 HashMap<String, Object> hashMap = Maps.newHashMap();
 hashMap.put("data", JSONObject.toJSONString(params));
 url = "http://mydomain/dataDownLoad.cgi?data={data}";
 json = restTemplate.getForObject(url, String.class, hashMap);
 System.out.println("get json : " + json);
 url = "http://mydomain/dataDownLoad.cgi";
 json = restTemplate.postForObject(url, hashMap, String.class);
 System.out.println("hasmap post json : " + json);

結(jié)果為:

get json : {'status': 0, 'statusInfo': {'global': 'OK'}, 'data': 'http://mydomain/dataDownLoad.cgi?downLoadData=358300d5f9e1cc512efc178caaa0b061'}

500 Internal Server Error

最后經(jīng)過(guò)另一位同學(xué)幫忙排查,發(fā)現(xiàn)RestTemplate在postForObject時(shí),不可使用HashMap。而應(yīng)該是MultiValueMap。改為如下:

MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("data", JSONObject.toJSONString(params));
url = "http://mydomain/dataDownLoad.cgi";
json = restTemplate.postForObject(url, paramMap, String.class);
System.out.println("post json : " + json);

結(jié)果為:

post json : {'status': 0, 'statusInfo': {'global': 'OK'}, 'data': 'http://mydomain/dataDownLoad.cgi?downLoadData=f2fc328513886e51b3b67d35043985ae'}

然后我想起之前使用RestTemplate發(fā)起post請(qǐng)求時(shí),使用POJO作為參數(shù),是可行的。再次測(cè)試:

url = "http://mydomain/dataDownLoad.cgi";
PostData postData = new PostData();
postData.setData(JSONObject.toJSONString(params));
json = restTemplate.postForObject(url, paramMap, String.class);
System.out.println("postData json : " + json);

返回:500 Internal Server Error。

到現(xiàn)在為止接口調(diào)通了。但問題的探究才剛剛開始。

RestTemplate的post參數(shù)為什么使用MultiValueMap而不能使用HashMap?

為什么post接口,get請(qǐng)求也可以正確返回?

為什么java服務(wù)端可以接收POJO參數(shù),python服務(wù)端不可以?python服務(wù)端使用CGI(Common Gateway Interface),與cgi有關(guān)系嗎?

何為MultiValueMap

IDEA中command+N,搜索類MultiValueMap,發(fā)現(xiàn)apache的commons-collections包有一個(gè)MultiValueMap類,spring-core包中有一個(gè)接口MultiValueMap,及其實(shí)現(xiàn)類LinkedMultiValueMap。顯然看spring包。

首先看LinkedMultiValueMap,實(shí)現(xiàn)MultiValueMap接口,只有一個(gè)域:Map<K, List<V>> targetMap = new LinkedHashMap<K, List<V>>()。 其中value為new LinkedList<V>()。再看接口方法:

public interface MultiValueMap<K, V> extends Map<K, List<V>> {
  V getFirst(K key); //targetMap.get(key).get(0)
  void add(K key, V value); //targetMap.get(key).add(value)
  void set(K key, V value); //targetMap.set(key, Lists.newLinkedList(value))
  void setAll(Map<K, V> values); //將普通map轉(zhuǎn)為L(zhǎng)inkedMultiValueMap
  Map<K, V> toSingleValueMap(); //只保留所有LinkedList的第一個(gè)值,轉(zhuǎn)為L(zhǎng)inkedHashMap
}

綜上,LinkedMultiValueMap實(shí)際就是Key-LinkedList的map。

RestTemplate怎么處理post參數(shù)

首先查看RestTemplate源碼,首先將請(qǐng)求封裝成HttpEntityRequestCallback類對(duì)象,然后再處理請(qǐng)求。

Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
    throws RestClientException {
  //請(qǐng)求包裝成httpEntityCallback
  RequestCallback requestCallback = httpEntityCallback(request, responseType);
  HttpMessageConverterExtractor<T> responseExtractor =
      new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
  //處理請(qǐng)求   
  return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}

那么HttpEntityRequestCallback是什么樣的呢?如下,實(shí)際是把請(qǐng)求數(shù)據(jù)放在了一個(gè)HttpEntity中。如果requestBody是HttpEntity類型,就直接轉(zhuǎn);否則,放在HttpEntity的body中。

//請(qǐng)求內(nèi)容封裝在一個(gè)HttpEntity對(duì)象中。
private HttpEntityRequestCallback(Object requestBody, Type responseType) {
  super(responseType);
  if (requestBody instanceof HttpEntity) {
    this.requestEntity = (HttpEntity<?>) requestBody;
  }
  else if (requestBody != null) {
    this.requestEntity = new HttpEntity<Object>(requestBody);
  }
  else {
    this.requestEntity = HttpEntity.EMPTY;
  }
}

接著看一下HttpEntity源碼:

public class HttpEntity<T> {
  private final HttpHeaders headers;
  private final T body;
  public HttpEntity(T body) {
    this.body = body;
  }
}
public class HttpHeaders implements MultiValueMap<String, String>, Serializable{
  ......
}

至此,與MultiValueMap聯(lián)系上了。

基于本次問題,我們不考慮post數(shù)據(jù)參數(shù)是HttpEntity類型的,只考慮普通POJO。那么,postForObject中對(duì)post數(shù)據(jù)的第一步處理,就是放在一個(gè)HttpEntity類型(header為MultiValueMap類型,body為泛型)的body中。

再看處理請(qǐng)求的部分:

Object requestBody = requestEntity.getBody();
Class<?> requestType = requestBody.getClass();
HttpHeaders requestHeaders = requestEntity.getHeaders();
MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
  if (messageConverter.canWrite(requestType, requestContentType)) {
    if (!requestHeaders.isEmpty()) {
      httpRequest.getHeaders().putAll(requestHeaders);
    }
    ((HttpMessageConverter<Object>) messageConverter).write(
        requestBody, requestContentType, httpRequest);
    return;
  }
}

通過(guò)配置的HttpMessageConverter來(lái)處理。

  <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
    <constructor-arg ref="ky.clientHttpRequestFactory"/>
    <property name="errorHandler">
      <bean class="org.springframework.web.client.DefaultResponseErrorHandler"/>
    </property>
    <property name="messageConverters">
      <list>
        <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
        <bean class="cn.com.autodx.common.jsonView.ViewAwareJsonMessageConverter"/>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
          <property name="supportedMediaTypes">
            <list>
              <value>text/html;charset=UTF-8</value>
              <value>application/json</value>
            </list>
          </property>
        </bean>
      </list>
    </property>
  </bean>

符合要求的只有ViewAwareJsonMessageConverter,其自定義處理如下。post數(shù)據(jù)中hashMap只含有data一個(gè)key,不含status字段,所以會(huì)跳過(guò)寫的操作,即post請(qǐng)求帶不上參數(shù)。如果修改代碼,當(dāng)不含status字段時(shí),按照父類方法處理,則服務(wù)端可以得到參數(shù)。

protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
  if(object instanceof Map) {
    Map map = (Map)object;
    HashMap statusInfo = new HashMap();
    //不含有status字段,跳過(guò)
    Object status = map.get("status");
    if(status != null) {
      int code = Integer.parseInt(String.valueOf(status));
      if(0 != code) {
        super.writeInternal(object, outputMessage);
      } else {
        statusInfo.put("global", "OK");
        map.put("statusInfo", statusInfo);
        super.writeInternal(object, outputMessage);
      }
    }
  } else {
    super.writeInternal(object, outputMessage);
  }
}

而使用MultiValueMap會(huì)由FormHttpMessageConverter正確處理。

首先判斷是否可以執(zhí)行寫操作,如果可以,執(zhí)行寫操作。

  @Override
  public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    if (!MultiValueMap.class.isAssignableFrom(clazz)) {
      return false;
    }
    if (mediaType == null || MediaType.ALL.equals(mediaType)) {
      return true;
    }
    for (MediaType supportedMediaType : getSupportedMediaTypes()) {
      if (supportedMediaType.isCompatibleWith(mediaType)) {
        return true;
      }
    }
    return false;
  }
@Override
@SuppressWarnings("unchecked")
public void write(MultiValueMap<String, ?> map, MediaType contentType, HttpOutputMessage outputMessage)
    throws IOException, HttpMessageNotWritableException {
  if (!isMultipart(map, contentType)) { //LinkedList中是否含有多個(gè)數(shù)據(jù)
    //只是普通的K-V,寫form
    writeForm((MultiValueMap<String, String>) map, contentType, outputMessage);
  }
  else {
    writeMultipart((MultiValueMap<String, Object>) map, outputMessage);
  }
}

既如此,那么post參數(shù)為POJO時(shí),如何呢?

POJO也會(huì)被ViewAwareJsonMessageConverter處理,在其writeInternal中,object不是map,所以調(diào)用 super.writeInternal(object, outputMessage),如下:

@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException,                              HttpMessageNotWritableException {
  OutputStream out = outputMessage.getBody();
  String text = JSON.toJSONString(obj, features);
  byte[] bytes = text.getBytes(charset);
  out.write(bytes);
}

如果注釋掉ViewAwareJsonMessageConverter,跟蹤發(fā)現(xiàn),會(huì)報(bào)錯(cuò),返回沒有合適的HttpMessageConverter處理。

使用ViewAwareJsonMessageConverter和使用FormHttpMessageConverter寫數(shù)據(jù)的格式是不一樣的,所以,post POJO后,會(huì)返回錯(cuò)誤,但實(shí)際已將參數(shù)傳遞出去。

所以,對(duì)于我們配置的RestTemplate來(lái)說(shuō),post參數(shù)可以是map(有字段要求),也可以是POJO。即,輸入輸出數(shù)據(jù)由RestTemplate配置的messageConverters決定。

至此,我們已經(jīng)清楚了第一個(gè)問題,剩下的問題同樣的思路。跟蹤一下getForObject的處理路徑。get方式請(qǐng)求時(shí),把所有的參數(shù)拼接在url后面,發(fā)給服務(wù)端,就可以把參數(shù)帶到服務(wù)端。

剩下的問題就是python服務(wù)端是怎么處理請(qǐng)求的。首先研究一下CGI。

何為CGI

通用網(wǎng)關(guān)接口(CGI,Common Gateway Interface)是一種Web服務(wù)器和服務(wù)器端程序進(jìn)行交互的協(xié)議。CGI完全獨(dú)立于編程語(yǔ)言,操作系統(tǒng)和Web服務(wù)器。這個(gè)協(xié)議可以用vb,c,php,python 來(lái)實(shí)現(xiàn)。

工作方式如圖所示:

browser->webServer: HTTP protocol

webServer->CGI腳本: 通過(guò)CGI管理模塊調(diào)用腳本

CGI腳本->CGI腳本: 執(zhí)行腳本程序

CGI腳本->webServer: 返回結(jié)果

webServer->browser: HTTP protocol

web服務(wù)器獲取了請(qǐng)求cgi服務(wù)的http請(qǐng)求后,啟動(dòng)cgi腳本,并將http協(xié)議參數(shù)和客戶端請(qǐng)求參數(shù)轉(zhuǎn)為cgi協(xié)議的格式,傳給cgi腳本。cgi腳本執(zhí)行完畢后,將數(shù)據(jù)返回給web服務(wù)器,由web服務(wù)器返回給客戶端。

cgi腳本怎么獲取參數(shù)呢?

CGI腳本從環(huán)境變量QUERY_STRING中獲取GET請(qǐng)求的數(shù)據(jù)

CGI腳本從stdin(標(biāo)準(zhǔn)輸入)獲取POST請(qǐng)求的數(shù)據(jù),數(shù)據(jù)長(zhǎng)度存在環(huán)境變量CONTENT_LENGTH中。

了解CGI大概是什么東東后,看一下python實(shí)現(xiàn)的CGI。

python的CGI模塊,要獲取客戶端的post參數(shù),可以使用cgi.FieldStorage()方法。FieldStorage相當(dāng)于python中的字典,支持多個(gè)方法??梢灾С忠话愕膋ey-value,也可以支持key-List<Value>,即類似于MultiValueMap形式的參數(shù)(如多選的表單數(shù)據(jù))。

至此,本問題主要是在于程序怎么傳遞參數(shù),對(duì)于spring restTemplate而言,就是messageConverters怎么配置的。

更多關(guān)于RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)問題文章大家看看下面的相關(guān)鏈接

相關(guān)文章

  • SpringBoot 工程中的異常處理方式

    SpringBoot 工程中的異常處理方式

    這篇文章主要介紹了SpringBoot 工程中的異常處理方式,幫助大家更好的理解和學(xué)習(xí)使用springboot框架,感興趣的朋友可以了解下
    2021-02-02
  • window系統(tǒng)安裝jdk jre的教程圖解

    window系統(tǒng)安裝jdk jre的教程圖解

    java開發(fā)少不了安裝jdk,jdk可以同時(shí)安裝多個(gè)版本,只要在項(xiàng)目部署時(shí)注意切換版本選擇,下面小編給大家?guī)?lái)了window系統(tǒng)安裝jdk jre的教程圖解,感興趣的朋友一起看看吧
    2018-08-08
  • Springboot @RequestBody注解踩坑記錄

    Springboot @RequestBody注解踩坑記錄

    這篇文章主要介紹了Springboot @RequestBody注解踩坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java 圖片復(fù)制功能實(shí)現(xiàn)過(guò)程解析

    Java 圖片復(fù)制功能實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了Java 圖片復(fù)制功能實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • java 動(dòng)態(tài)增加定時(shí)任務(wù)示例

    java 動(dòng)態(tài)增加定時(shí)任務(wù)示例

    本篇文章主要介紹了java 動(dòng)態(tài)增加定時(shí)任務(wù)示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • Java格式化類Format解讀

    Java格式化類Format解讀

    Java.text.Format 是 Java 格式化的抽象基類,用于將對(duì)象格式化為指定模式的字符串,或者將指定模式的字符串解析為對(duì)象,主要包含 format 和 parseObject 方法,分別用于格式化和解析
    2024-12-12
  • Java中鍵盤輸入的幾種常見方式小結(jié)

    Java中鍵盤輸入的幾種常見方式小結(jié)

    本文主要介紹了Java中鍵盤輸入的幾種常見方式小結(jié),主要是三種方式IO流、Scanner類、BufferedReader寫入,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • 一天時(shí)間用Java寫了個(gè)飛機(jī)大戰(zhàn)游戲,朋友直呼高手

    一天時(shí)間用Java寫了個(gè)飛機(jī)大戰(zhàn)游戲,朋友直呼高手

    前兩天我發(fā)現(xiàn)論壇有兩篇飛機(jī)大戰(zhàn)的文章異常火爆,但都是python寫的,竟然不是我大Java,說(shuō)實(shí)話作為老java選手,我心里是有那么一些失落的,今天特地整理了這篇文章,需要的朋友可以參考下
    2021-05-05
  • 使用ByteArrayOutputStream寫入字符串方式

    使用ByteArrayOutputStream寫入字符串方式

    這篇文章主要介紹了使用ByteArrayOutputStream寫入字符串方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 基于logback.xml不生效問題的解決

    基于logback.xml不生效問題的解決

    這篇文章主要介紹了基于logback.xml不生效問題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評(píng)論

顺平县| 容城县| 盐亭县| 昔阳县| 女性| 九台市| 浦县| 增城市| 贵港市| 恩平市| 虞城县| 瑞金市| 昌图县| 怀来县| 普洱| 大理市| 施秉县| 咸宁市| 伊金霍洛旗| 乌恰县| 庄浪县| 汨罗市| 横山县| 通渭县| 阿拉尔市| 漳浦县| 西林县| 洱源县| 乌苏市| 古丈县| 甘洛县| 美姑县| 利津县| 宝应县| 泰安市| 桑日县| 洛扎县| 宜宾市| 湘潭县| 福清市| 广丰县|