You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PHP向Microsoft Business Central发起POST请求时遇400错误求助

Microsoft Business Central API PHP请求异常排查

问题场景

通过Postman调用Microsoft Business Central API创建新客户完全正常,但使用PHP发起POST请求时出现两种异常:

  1. 使用file_get_contents发起请求时返回400错误
  2. 改用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);

额外排查建议

  1. 对比Postman和PHP请求的所有头信息:包括Content-Length、Accept等,确保完全一致
  2. 检查$content中的字段:是否有必填字段遗漏,字段名拼写是否与API文档完全匹配(注意大小写)
  3. 验证$accessToken的有效性:确保令牌未过期,且拥有创建客户的权限

内容的提问来源于stack exchange,提问作者Trisse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 07:17:06