CodeIgniter 4更换FedEx账号调用Rates API遇400错误及异常处理问题
关于FedEx Rates and Transit API调用的两个问题
我正在开发一个调用FedEx Rates and Transit API获取详情的程序,用开发者门户中与API项目关联的账号时API能正常工作,但换成其他账号后,API返回以下错误:
file_get_contents(https://apis.fedex.com/rate/v1/rates/quotes): Failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request
我在API文档和网络搜索里没找到足够相关信息,有两个问题:
- API是不是有限制,只允许调用关联账号的数据?还是需要调整请求参数?
- 我已经用try-catch块处理错误,但这个错误还是导致程序崩溃,有没有可行的处理方式?
public function callAPI() { $this->accountNumber->set_value('11111111'); //set shipper postalcode and country code $this->shipper->set_postalCode(12345); $this->shipper->set_countryCode('US'); $this->shipper->set_residential(false); //set recipient postalcode and country code $this->recipient->set_postalCode(456789); $this->recipient->set_countryCode('US'); $this->recipient->set_residential(true); //set weight and units $this->weight->set_units('LB'); $this->weight->set_value(26); $this->requestedShipment->set_shipper(['address' => $this->shipper]); $this->requestedShipment->set_recipient(['address' => $this->recipient]); $this->requestedShipment->set_requestedPackageLineItems(array(['weight' => $this->weight])); //set pickup type //types: "CONTACT_FEDEX_TO_SCHEDULE", "DROPOFF_AT_FEDEX_LOCATION", "USE_SCHEDULED_PICKUP" $this->requestedShipment->set_pickupType("USE_SCHEDULED_PICKUP"); // set type of rate to be returned //types: "LIST", "INCENTIVE", "ACCOUNT", "PREFERRED" (see documentation for explanations) $this->requestedShipment->set_rateRequestType(['ACCOUNT']); //set shipment date $this->requestedShipment->set_shipDateStamp("2023-08-17"); $this->rateRequestControlParameters->set_return_tranist(true); $accountNumberJSON = json_encode($this->accountNumber); $requestedShipmentJSON = json_encode($this->requestedShipment); $url = "https://apis.fedex.com/oauth/token"; //client id is api key and client secret is secret key $data = [ 'grant_type' => 'client_credentials', 'client_id' => 'my_clinet_id', 'client_secret' => 'my_clinet_secret' ]; $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded", 'method' => 'POST', 'content' => http_build_query($data) ) ); $context = stream_context_create($options); $resp = file_get_contents($url, false, $context); $response_data = json_decode($resp); $access_token = $response_data->access_token; //calls function if token is working (token changes every minute) if (isset($access_token)) { echo $this->get_rates($access_token, $this->accountNumber, $this->requestedShipment, $this->rateRequestControlParameters); } } function get_rates($token, $acct, $ship, $rateReqCtrlPara) { $url = "https://apis.fedex.com/rate/v1/rates/quotes"; $data = ['accountNumber' => $acct, 'requestedShipment' => $ship, 'rateRequestControlParameters' => $rateReqCtrlPara, 'carrierCodes' => array('FDXG') ]; $options = array( 'http' => array( 'header' => [ "Content-type: application/json", "Authorization: Bearer {$token}", "X-locale: en_US" ], 'method' => 'POST', 'content' => json_encode($data) ) ); $context = stream_context_create($options); try{ $resp_data = file_get_contents($url, false, $context); //error here } catch (exception $e) { echo $e; } return $resp_data; }
问题1解答:API限制与请求参数调整
FedEx的API确实存在账号关联限制:
- 开发者门户创建的API项目(对应
client_id和client_secret)默认仅关联一个FedEx账号,使用其他未关联账号调用时会触发权限校验失败,返回400错误。 - 若要使用多个账号,需在FedEx开发者门户中为当前API项目添加额外关联账号,或确保目标账号已被授权通过当前API密钥访问。
同时需检查以下请求参数关键点:
- 账号结构:确认
accountNumber是否包含countryCode字段,部分场景下API要求完整账号信息而非仅数值。 - 取件类型匹配:若新账号未开通
USE_SCHEDULED_PICKUP服务,该参数会导致请求失败,可先替换为DROPOFF_AT_FEDEX_LOCATION测试。 - 费率类型权限:
rateRequestType设为ACCOUNT时,目标账号需具备对应协议费率权限,否则会触发参数无效。
问题2解答:try-catch无法捕获错误的处理方式
file_get_contents在HTTP请求失败时默认仅返回false并触发PHP警告,不会主动抛出异常,因此你的try-catch块无法捕获该错误。推荐两种解决方式:
方式1:将警告转换为可捕获异常
在调用file_get_contents前,临时修改错误处理逻辑,将警告转为异常:
function get_rates($token, $acct, $ship, $rateReqCtrlPara) { $url = "https://apis.fedex.com/rate/v1/rates/quotes"; $data = ['accountNumber' => $acct, 'requestedShipment' => $ship, 'rateRequestControlParameters' => $rateReqCtrlPara, 'carrierCodes' => array('FDXG') ]; $options = array( 'http' => array( 'header' => [ "Content-type: application/json", "Authorization: Bearer {$token}", "X-locale: en_US" ], 'method' => 'POST', 'content' => json_encode($data), 'ignore_errors' => true // 避免直接抛出警告 ) ); $context = stream_context_create($options); // 临时将警告转为异常 set_error_handler(function($errno, $errstr) { throw new Exception($errstr); }); try{ $resp_data = file_get_contents($url, false, $context); // 检查HTTP状态码 foreach($http_response_header ?? [] as $header) { if(strpos($header, 'HTTP/1.1') === 0) { $status = substr($header, 9, 3); if($status != 200) { throw new Exception("API请求失败,状态码:{$status},响应内容:{$resp_data}"); } } } } catch (Exception $e) { echo $e->getMessage(); $resp_data = null; } finally { restore_error_handler(); // 恢复默认错误处理 } return $resp_data; }
方式2:改用cURL替代file_get_contents
cURL对HTTP错误的处理更灵活,无需依赖错误转换,直接捕获请求异常:
function get_rates($token, $acct, $ship, $rateReqCtrlPara) { $url = "https://apis.fedex.com/rate/v1/rates/quotes"; $data = json_encode(['accountNumber' => $acct, 'requestedShipment' => $ship, 'rateRequestControlParameters' => $rateReqCtrlPara, 'carrierCodes' => array('FDXG') ]); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Content-type: application/json", "Authorization: Bearer {$token}", "X-locale: en_US" ]); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $resp_data = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if($http_code != 200) { $error = curl_error($ch); echo "请求失败:{$error},状态码:{$http_code},响应:{$resp_data}"; $resp_data = null; } curl_close($ch); return $resp_data; }
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

