使用PHP向Microsoft Business Central发起POST请求时遇400错误求助
Microsoft Business Central API PHP请求异常排查
问题场景
通过Postman调用Microsoft Business Central API创建新客户完全正常,但使用PHP发起POST请求时出现两种异常:
- 使用
file_get_contents发起请求时返回400错误 - 改用cURL后请求返回200状态码,但Business Central中并未创建客户
原代码及问题分析
1. file_get_contents 实现(400错误)
$url = "https://api.businesscentral.dynamics.com/v2.0/ ". $tenantId ."/Production/api/v2.0/customers"; $content = array( "displayName" => $obj->displayName, "addressLine1" => $obj->addressLine1, "addressLine2" => $obj->addressLine2, "city" => $obj->city, "state" => $obj->state, "country" => $obj->country, "postalCode" => $obj->postalCode, "email" => $obj->email, "currencyCode" => $obj->currencyCode ); $options = array( "http" => array( "header" => "Content-Type: application/json " . "Authorization: Bearer " . $accessToken . " ", "method" => "POST", "content" => json_encode($content) ) ); $context = stream_context_create($options); $json = file_get_contents($url, false, $context); $data = json_decode($json, true);
核心问题点:
- URL存在多余空格:
https://api.businesscentral.dynamics.com/v2.0/末尾的空格会导致请求地址无效,直接触发400错误 - 请求头换行符不兼容:使用的纯换行符
\n在部分环境下可能不被识别,建议改用标准HTTP头换行符\r\n - 未校验
json_encode结果:如果$content包含非UTF-8字符,json_encode会失败,导致请求体为空或无效,触发400
2. cURL 实现(返回200但未创建客户)
$ch = curl_init($url); # Setup request to send json via POST. curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Authorization: Bearer ' . $accessToken)); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($content)); # Return response instead of printing. curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); # Send request. $data = curl_exec($ch); if (curl_errno($ch)) { $error_msg = curl_error($ch); } curl_close($ch); // Do something with the $data response var_dump($data);
核心问题点:
- 继承了URL的多余空格问题:同样会导致请求到错误的端点,即使返回200也不是目标API的响应
CURLOPT_RETURNTRANSFER设为false:会直接输出响应内容,导致$data无法获取API的真实返回,无法排查实际问题(比如API可能返回了成功但字段不合法,或者是其他非创建客户的响应)- 未开启详细日志:无法对比Postman的请求细节,难以定位字段或头信息的差异
修正后的代码示例
1. 修正后的file_get_contents代码
// 移除URL中的多余空格 $url = "https://api.businesscentral.dynamics.com/v2.0/" . $tenantId . "/Production/api/v2.0/customers"; $content = array( "displayName" => $obj->displayName, "addressLine1" => $obj->addressLine1, "addressLine2" => $obj->addressLine2, "city" => $obj->city, "state" => $obj->state, "country" => $obj->country, "postalCode" => $obj->postalCode, "email" => $obj->email, "currencyCode" => $obj->currencyCode ); // 校验JSON编码结果 $jsonContent = json_encode($content); if ($jsonContent === false) { die("JSON编码失败:" . json_last_error_msg()); } $options = array( "http" => array( "header" => "Content-Type: application/json\r\n" . "Authorization: Bearer " . $accessToken . "\r\n", "method" => "POST", "content" => $jsonContent, // 捕获错误响应内容 "ignore_errors" => true ) ); $context = stream_context_create($options); $json = file_get_contents($url, false, $context); // 输出响应状态码和内容,便于排查 $httpStatus = $http_response_header[0]; echo "响应状态:" . $httpStatus . "\n"; var_dump($json); $data = json_decode($json, true);
2. 修正后的cURL代码
// 移除URL中的多余空格 $url = "https://api.businesscentral.dynamics.com/v2.0/" . $tenantId . "/Production/api/v2.0/customers"; $content = array( "displayName" => $obj->displayName, "addressLine1" => $obj->addressLine1, "addressLine2" => $obj->addressLine2, "city" => $obj->city, "state" => $obj->state, "country" => $obj->country, "postalCode" => $obj->postalCode, "email" => $obj->email, "currencyCode" => $obj->currencyCode ); // 校验JSON编码结果 $jsonContent = json_encode($content); if ($jsonContent === false) { die("JSON编码失败:" . json_last_error_msg()); } $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Authorization: Bearer ' . $accessToken )); curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonContent); // 设置为true,将响应内容返回给变量 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 开启详细日志,对比Postman请求细节 curl_setopt($ch, CURLOPT_VERBOSE, true); $verboseLog = fopen('php://temp', 'w+'); curl_setopt($ch, CURLOPT_STDERR, $verboseLog); $data = curl_exec($ch); // 输出详细请求日志 rewind($verboseLog); $logContent = stream_get_contents($verboseLog); echo "请求日志:\n" . $logContent . "\n"; // 捕获cURL错误 if (curl_errno($ch)) { echo "cURL错误:" . curl_error($ch) . "\n"; } // 输出响应状态码 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); echo "响应状态码:" . $httpCode . "\n"; var_dump($data); curl_close($ch); fclose($verboseLog);
额外排查建议
- 对比Postman和PHP请求的所有头信息:包括
Content-Length、Accept等,确保完全一致 - 检查
$content中的字段:是否有必填字段遗漏,字段名拼写是否与API文档完全匹配(注意大小写) - 验证
$accessToken的有效性:确保令牌未过期,且拥有创建客户的权限
内容的提问来源于stack exchange,提问作者Trisse
相关产品推荐
相关产品推荐

