PHP 中的 SOAP 错误:OperationFormatter 遇到无效的消息体 [英] SOAP Error in PHP: OperationFormatter encountered an invalid Message body

查看:42
本文介绍了PHP 中的 SOAP 错误:OperationFormatter 遇到无效的消息体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

原始问题:

我正在尝试从 SOAP API wsdl 链接获取数据.我的代码如下.但我收到这个错误.任何人都可以帮忙吗?

I'm trying to get data from SOAP API wsdl link. My code as below. But I get this error. Can anyone please help with this?

错误消息:OperationFormatter 遇到无效的消息正文.期望找到名称为GetLocalRates"的节点类型Element"和命名空间http://tempuri.org/".找到节点类型元素"的名称'soapenv:Envelope' 和命名空间'http://www.w3.org/2003/05/soap-envelope'

Error Message: OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name 'GetLocalRates' and namespace 'http://tempuri.org/'. Found node type 'Element' with name 'soapenv:Envelope' and namespace 'http://www.w3.org/2003/05/soap-envelope'

<?php
$api_link = 'https://www.my-api-link.com/RateAPI.svc/SSL?wsdl';

//setting xml request to api
$request = '<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:ezr="http://schemas.datacontract.org/2004/07/EzremitAPI.Entities">
              <soapenv:Body>
                 <tem:GetLocalRates>
                     <tem:credentials>
                         <ezr:AgentCode>####</ezr:AgentCode>
                         <ezr:HashedPassword>####</ezr:HashedPassword>
                         <ezr:Username>####</ezr:Username>
                     </tem:credentials>
                     <tem:payincurcode>####</tem:payincurcode>
                     <tem:transferType>####</tem:transferType>
                 </tem:GetLocalRates>
              </soapenv:Body>
            </soapenv:Envelope>';

try {
$client = new SoapClient($api_link, array('cache_wsdl' => WSDL_CACHE_NONE, 'soap_version' => SOAP_1_2, 'reliable' => 1.2 , 'useWSA' => TRUE ) );
$soapaction = "http://tempuri.org/IRateAPI/GetLocalRates";
$client->soap_defencoding = 'UTF-8';
// Apply WSA headers
$headers = array();
$headers[] = new SoapHeader('http://www.w3.org/2005/08/addressing', 'To', 'https://www.my-api-link.com/RateAPI.svc/SSL?wsdl', true);
$headers[] = new SoapHeader('http://www.w3.org/2005/08/addressing', 'Action', 'http://tempuri.org/IRateAPI/GetLocalRates', true);
$client->__setSoapHeaders($headers);

$response = $client->GetLocalRates(new SoapVar($request, XSD_ANYXML));
print_r($response);

}
  catch(Exception $e) {
    echo $e->getMessage();
}
?>

编辑 1(根据第一条评论修改代码)

Edit 1 (Code amended as per to 1st comment)

结果:

http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/faults:Receivera:InternalServiceFaultOperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name 'GetLocalRates' and namespace 'http://tempuri.org/'. Found node type 'Element' with name 'soapenv:Envelope' and namespace 'http://www.w3.org/2003/05/soap-envelope'OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name 'GetLocalRates' and namespace 'http://tempuri.org/'. Found node type 'Element' with name 'soapenv:Envelope' and namespace 'http://www.w3.org/2003/05/soap-envelope' at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)
 at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)
 at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters)
 at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
 at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)System.Runtime.Serialization.SerializationException

编辑 2:

$client->__getTypes() 结果:

array(8) {
  [0]=>
  string(84) "struct EzCredential {
 string AgentCode;
 string HashedPassword;
 string Username;
}"
  [1]=>
  string(58) "struct ArrayOfCurrencyRate {
 CurrencyRate CurrencyRate;
}"
  [2]=>
  string(187) "struct CurrencyRate {
 decimal AgentMargin;
 string CurrencyCode;
 string CurrencyDescription;
 decimal FromAmount;
 decimal Rate;
 string RateType;
 decimal ToAmount;
 string Trantype;
}"
  [3]=>
  string(95) "struct GetLocalRates {
 EzCredential credentials;
 string payincurcode;
 string transferType;
}"
  [4]=>
  string(74) "struct GetLocalRatesResponse {
 ArrayOfCurrencyRate GetLocalRatesResult;
}"
  [5]=>
  string(8) "int char"
  [6]=>
  string(17) "duration duration"
  [7]=>
  string(11) "string guid"
}

$client->__getFunctions() 结果:

array(1) {
  [0]=>
  string(62) "GetLocalRatesResponse GetLocalRates(GetLocalRates $parameters)"
}

已修复:在下面使用而不是 XML 信封.非常感谢,@Marcel.你是一个伟大的救世主.

Fixed: Used below instead of the XML envelope. Thanks a lot, @Marcel. You are a great saviour.

$requestParams = array( 'credentials' => array('AgentCode' => $acode,
                                               'HashedPassword' => $hpass,
                                               'Username' => $uname),
                  'payincurcode' => $ccode,
                  'transferType' => $ttype
              );

$response = $client->GetLocalRates( $requestParams );

推荐答案

提前感谢您使用缺失的数据更新了您的问题.这个例子是一个原生的 php 例子,它展示了如何以面向对象的方式使用肥皂函数和类型.

Thank you in advance that you have updated your question with the missing data. This example is a native php example that shows how to work with soap functions and types in an object orientated way.

1.类型和类

正如您在 types 数组中看到的那样,有几种类型声明为 struct.如果您喜欢这样说,结构体可以称为 PHP 类.所以让我们从每个结构体中创建一个单独的 php 类.

As you can see in your types array, there are several types declared as struct. If you like to say so, structs can be called PHP classes. So lets make a single php class out of every struct.

class EzCredentialStruct
{
    public $AgentCode;

    public $HashedPassword;

    public $Username;

    public function getAgentCode() : string
    {
        return $this->AgentCode;
    }

    public function setAgentCode(string $AgentCode) : EzCredentialStruct
    {
        $this->AgentCode = $AgentCode;
        return $this;
    }

    public function getHashedPassword() : string
    {
        return $this->HashedPassword;
    }

    public function setHashedPassword(string $HashedPassword) : EzCredentialStruct
    {
        $this->HashedPassword = $HashedPassword;
        return $this;
    }

    public function getUsername() : string
    {
        return $this->Username;
    }

    public function setUsername(string $Username) : EzCredentialStruct
    {
        $this->Username = $Username;
        return $this;
    }
}

class GetLocalRatesStruct
{
    public $credentials;

    public $payincurcode;

    public $transferType;

    public function getCredentials() : EzCredentialStruct
    {
        return $this->credentials;
    }

    public function setCredentials(EzCredentialStruct $credentials) : GetLocalRatesStruct
    {
        $this->credentials = $credentials;
        return $this;
    }

    public function getPayincurcode() : string
    {
        return $this->payincurcode;
    }

    public function setPayincurcode(string $payincurcode) : GetLocalRatesStruct
    {
        $this->payincurcode = $payincurcode;
        return $this;
    }

    public function getTransferType() : string
    {
        return $this->transferType;
    }

    public function setTransferType(string $transferType) : GetLocalRatesStruct
    {
        $this->transferType = $transferType;
        return $this;
    }
}

这两个类是类型数组中所有结构的示例.所以把你所有的结构写成类.稍后您会注意到好处.

These two classes are examples of all structs from your types array. So write down all your structs as classes. You will notice the benefit later.

2.Soap 客户端和类映射选项

现在,当我们将 Web 服务的使用类型声明为类时,我们可以启动soap 客户端.使用正确的选项启动soap 客户端很重要.始终将客户端封装在 try/catch 块中.

Now, as we hace declared the used types of the webservice as classes, we can initiate the soap client. It is important to initiate the soap client with the right options. Always wrap up the client in a try/catch block.

try {
    $options = [
        'cache_wsdl' => WSDL_CACHE_NONE, 
        'soap_version' => SOAP_1_2,
        'trace' => true,
        'classmap' => [
            'EzCredential' => 'EzCredentialStruct',
            'GetLocalRates' => 'GetLocalRatesStruct',
        ],
    ];

    $wsdl = 'path/to/the/webservice/endpoint?wsdl';

    $client = new \SoapClient(
        $wsdl,
        $options,
    );
} catch (\SoapFault $e) {
    // error handling
}

你可以看到 options 数组中有一个 classmap 键.classmap 将类型路由到特定的 php 类.在这个例子中,我们只使用我们之前定义的两个示例类型类.soap 客户端现在可以自动创建 web 服务需要的 xml 字符串.

AS you can see there is a classmap key in the options array. The classmap routes types to specific php classes. In this example we only use the two example type classes we defined earlier. The soap client can now automatically create the xml string the webservice needs.

3.把它们放在一起

现在,我们拥有了正确的soap请求所需的一切,我们的代码应该如下所示.

Now, as we have all we need for a proper soap request, our code should look as follows.

$credentials = (new EzCredentialStruct())
    ->setAgentCode($agentCode)
    ->setHashedPassword($hashedPassword)
    ->setUsername($username);

$request = (new GetLocalRatesStruct())
    ->setCredentials($credentials)
    ->setPayincurcode($code)
    ->setTransferType($transferType);

$result = $client->GetLocalRates($request);

结论

乍一看,这种方法可能看起来更工作.但是,对于 webservice 声明的每个结构,将类型与函数和编程类分开是有意义的.webservice 函数的调用会更容易,因为我们只传递一个对象作为参数.不再有奇怪的数组结构.一切都在它的位置,可以重复使用.

At first glance, this approach may look like more work. But it makes sense to separate the types from the functions and programming classes for each struct the webservice declares. The call of the webservice function will be easier, because we only pass an object as a parameter. No more weird array constructs. Everything is in its place and can be reused.

PS:此代码未经测试.不建议在生产中使用它.此代码仅用作示例.它可能包含错误.

PS: This code is not tested. It is not recommended using it in production. This code is intended as an example only. It may contain errors.

这篇关于PHP 中的 SOAP 错误:OperationFormatter 遇到无效的消息体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆