PHP利用curl發起Http請求的400錯誤分析

背景

現象一-php封裝的http請求接口調用url

php的curl_exec接口調用結果.png

1.response的rawheader值顯示400錯誤php

HTTP/1.1 400 Unknown Version
Server: Tengine/2.0.3
Date: Tue, 19 Jul 2016 07:38:15 GMT
Content-Length: 0
Connection: keep-alive

2.本身封裝的發送http請求接口html

/**
* 發送httpful get請求
* @param  [array] $params [description]
* @return [object]         [description]
*/
public  function sendGetRequest($serverUrl,$params){
    try{
        $str_params = '';
        foreach ($params as $key => $value) {
            $str_params .= "&$key=".$value;
        }
        
        $response =  \Httpful\Request::get($serverUrl.$str_params)->send();
        return  $response->body;
    }catch(Execption $e){
        return array("statuscode"=>'-1',"message"=>'服務器出錯了');
    }
    
}

3.Request.php封裝的Request對象的send方法json

nategood/httpful中的Request::send方法使用php的curl_exec接口發起http請求。api

/**
* Actually send off the request, and parse the response
* @return string|associative array of parsed results
* @throws ConnectionErrorException when unable to parse or communicate w server
*/
public function send()
{
    if (!$this->hasBeenInitialized())
        $this->_curlPrep();

    $result = curl_exec($this->_ch);

    if ($result === false) {
        if ($curlErrorNumber = curl_errno($this->_ch)) {
            $curlErrorString = curl_error($this->_ch);
            $this->_error($curlErrorString);
            throw new ConnectionErrorException('Unable to connect: ' . $curlErrorNumber . ' ' . $curlErrorString);
        }

        $this->_error('Unable to connect.');
        throw new ConnectionErrorException('Unable to connect.');
    }

    $info = curl_getinfo($this->_ch);

    // Remove the "HTTP/1.x 200 Connection established" string and any other headers added by proxy
    $proxy_regex = "/HTTP\/1\.[01] 200 Connection established.*?\r\n\r\n/s";
    if ($this->hasProxy() && preg_match($proxy_regex, $result)) {
        $result = preg_replace($proxy_regex, '', $result);
    }

    $response = explode("\r\n\r\n", $result, 2 + $info['redirect_count']);

    $body = array_pop($response);
    $headers = array_pop($response);

    curl_close($this->_ch);

    return new Response($body, $headers, $this, $info);
}

4.curl_exec調用前url值瀏覽器

http://192.168.59.146/api?version=1.0&format=json&appkey=KtSNKxk3&access_token=changyanyun&method=pan.file.export&uid=3062000039000412278&fileId=3aaaa5c8-3eaa-4511-91e7-46831d418f10&fileIndex={"lifecycle":{"auditstatus":"0"},"general":{"source":"UGC","creator":"\u6559\u5e080523","uploader":"gsres_iflytek_f968bca78360d38abcbaf23a5a318b12","extension":"ppt","title":"Unit12 What did you do last weekdend\u8bfe\u65f64\uff081\uff09"},"properties":{"subject":["01"],"edition":["01"],"stage":["010001"],"book":["01010101-001"],"unit":["01"],"course":[""],"unit1":["01"],"unit2":[""],"unit3":[""],"phase":["03"],"type":["0100"],"rrtlevel1":["08"]}}

現象二-瀏覽器中直接訪問url

瀏覽器直接訪問url接口調用結果.png

http://192.168.59.146/api?version=1.0&format=json&appkey=KtSNKxk3&access_token=changyanyun&method=pan.file.export&uid=3062000039000412278&fileId=3aaaa5c8-3eaa-4511-91e7-46831d418f10&fileIndex={ %22lifecycle%22:{ %22auditstatus%22:%220%22},%22general%22:{ %22source%22:%22UGC%22,%22creator%22:%22\u6559\u5e080523%22,%22uploader%22:%22gsres_iflytek_f968bca78360d38abcbaf23a5a318b12%22,%22extension%22:%22ppt%22,%22title%22:%22Unit12%20What%20did%20you%20do%20last%20weekdend\u8bfe\u65f64\uff081\uff09%22},%22properties%22:{ %22subject%22:[%2201%22],%22edition%22:[%2201%22],%22stage%22:[%22010001%22],%22book%22:[%2201010101-001%22],%22unit%22:[%2202%22],%22course%22:[%22%22],%22unit1%22:[%2202%22],%22unit2%22:[%22%22],%22unit3%22:[%22%22],%22phase%22:[%2203%22],%22type%22:[%220100%22],%22rrtlevel1%22:[%2208%22]}}

緣由分析

對比現象一和現象二中的url值,能夠發現:服務器

  1. fileIndex參數值爲json字符串
  2. fileIndex參數值中的title屬性值中的空格字符在現象二中被編碼爲%20,且雙引號"被編碼爲%22,此操做爲瀏覽器自動執行
  3. 將url中的空格進行編碼後,curl_exec接口返回200

測試代碼

<?php
// 建立一個新cURL資源
$ch = curl_init();

$url = 'http://192.168.59.146/api?version=1.0&format=json&appkey=KtSNKxk3&access_token=changyanyun&method=pan.file.export&uid=3062000039000412278&fileId=3aaaa5c8-3eaa-4511-91e7-46831d418f10&fileIndex={"lifecycle":{"auditstatus":"0"},"general":{"source":"UGC","creator":"\u6559\u5e080523","uploader":"gsres_iflytek_f968bca78360d38abcbaf23a5a318b12","extension":"ppt","title":"Unit12 What did you do last weekdend\u8bfe\u65f64\uff081\uff09"},"properties":{"subject":["01"],"edition":["01"],"stage":["010001"],"book":["01010101-001"],"unit":["01"],"course":[""],"unit1":["01"],"unit2":[""],"unit3":[""],"phase":["03"],"type":["0100"],"rrtlevel1":["08"]}}';

//對url中的空格進行轉義
if(isset($_GET["urlencode"])){
	$url = str_replace(" ", '%20', $url);
}

var_dump("url");
var_dump($url);
// 設置URL和相應的選項
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);

// 抓取URL並把它傳遞給瀏覽器
$result = curl_exec($ch);
var_dump("result");
var_dump($result);
if ($result === false) {
	if ($curlErrorNumber = curl_errno($this->_ch)) {
		$curlErrorString = curl_error($this->_ch);
		$this->_error($curlErrorString);
		throw new ConnectionErrorException('Unable to connect: ' . $curlErrorNumber . ' ' . $curlErrorString);
	}

	$this->_error('Unable to connect.');
	throw new ConnectionErrorException('Unable to connect.');
}
var_dump("ch");
var_dump($ch);

$info = curl_getinfo($ch);
var_dump("info");
var_dump($info);
// Remove the "HTTP/1.x 200 Connection established" string and any other headers added by proxy
$proxy_regex = "/HTTP\/1\.[01] 200 Connection established.*?\r\n\r\n/s";
if (preg_match($proxy_regex, $result)) {
	$result = preg_replace($proxy_regex, '', $result);
}

$response = explode("\r\n\r\n", $result, 2 + $info['redirect_count']);
var_dump("response");
var_dump($response);

$body = array_pop($response);
var_dump("body");
var_dump($body);
$headers = array_pop($response);
var_dump("headers");
var_dump($headers);


// 關閉cURL資源,而且釋放系統資源
curl_close($ch);
?>

百分號編碼(即url編碼)

通常來講,URL只能使用英文字母、阿拉伯數字和某些標點符號,不能使用其餘文字和符號。阮一峯在他的博文中提到,網絡標準RFC 1738作了硬性規定:網絡

"...Only alphanumerics [0-9a-zA-Z], the special characters "$-.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL." <br>"只有字母和數字[0-9a-zA-Z]、一些特殊符號"$-.+!*'(),"[不包括雙引號]、以及某些保留字,才能夠不通過編碼直接用於URL。"app

url編碼是特定上下文的統一資源定位符 (URL)的編碼機制,服務器解析http請求時若是遇到非標準字符,會對該非標準字符進行編碼curl

總結

當http請求出現400錯誤時,通常是http請求無效(bad request),url參數編碼格式不正確致使。而url是有必定格式要求的,通常只能使用英文字母、阿拉伯數字和一些特殊字符,其餘字符如空格和雙引號須要通過編碼後才能用戶url。post

  • 在瀏覽器中直接訪問時瀏覽器會自動進行編碼處理
  • 可是在程序中直接調用時則須要事先對url進行編碼,如php使用curl_exec調用時須要對空格等非法字符進行編碼
  • 特別是針對url參數中包含json數據值須要多加註意。

本文同步在個人原創博客中。


廣告時間

這兩年最重要的感悟就是,無論工做怎樣,都要好好對本身,特別是本身的身體;女朋友很體諒我成天對着電腦工做,讓我能天天堅持吃一個蘋果;最近還開了一家網店,淘寶店鋪名稱叫碭山水果經營店8,她家有個蘋果園,每一年10月份是蘋果的收穫季節,剛摘下的蘋果新鮮可口,又甜又脆,並且價格實惠,如今是10斤35元包郵哦;若是你們讀到這裏,但願你們前往淘寶店鋪看看,同時也但願你們能捧場^-^,在此先謝謝各位童鞋啦.

淘寶店鋪二維碼 enter image description here

相關文章
相關標籤/搜索