Ошибка SOAP в PHP: OperationFormatter обнаружил недопустимое тело сообщения - PullRequest
0 голосов
/ 14 мая 2018

Оригинальный вопрос:

Я пытаюсь получить данные по ссылке SOAP API wsdl.Мой код, как показано ниже.Но я получаю эту ошибку.Может кто-нибудь, пожалуйста, помогите с этим?

Сообщение об ошибке: OperationFormatter обнаружил недопустимое тело сообщения.Ожидается найти тип узла "Элемент" с именем "GetLocalRates" и пространством имен "http://tempuri.org/'. Найден тип узла" Элемент "с именем" soapenv: Envelope "и пространством имен" 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 (Код изменен согласно первому комментарию)

Результаты:

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.Вы великий спаситель.

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

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

1 Ответ

0 голосов
/ 14 мая 2018

Заранее благодарим вас за то, что вы обновили свой вопрос недостающими данными. Этот пример является собственным примером php, который показывает, как работать с мыльными функциями и типами объектно-ориентированным способом.

1. Типы и классы

Как вы можете видеть в массиве типов, есть несколько типов, объявленных как struct . Если вы хотите так сказать, структуры могут называться классами PHP. Итак, давайте создадим один класс php из каждой структуры.

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;
    }
}

Эти два класса являются примерами всех структур из вашего массива типов. Поэтому запишите все свои структуры как классы. Вы заметите выгоду позже.

2. Soap Client и опция карты классов

Теперь, когда мы объявили используемые типы веб-службы в качестве классов, мы можем запустить мыльный клиент. Важно запустить клиент мыла с правильными опциями. Всегда заключайте клиента в блок try / catch.

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
}

КАК вы можете видеть, что в массиве опций есть ключ classmap. Карта классов направляет типы к определенным классам php. В этом примере мы используем только два примера типов классов, которые мы определили ранее. Теперь мыльный клиент может автоматически создавать строку xml, необходимую для веб-службы.

3. Положите все вместе

Теперь, когда у нас есть все, что нужно для правильного запроса мыла, наш код должен выглядеть следующим образом.

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

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

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

Заключение

На первый взгляд такой подход может показаться более трудоемким. Но имеет смысл отделить типы от функций и классов программирования для каждой структуры, которую объявляет веб-служба. Вызов функции веб-сервиса будет проще, потому что мы только передаем объект в качестве параметра. Нет больше странных конструкций массива. Все на своем месте и может быть использовано повторно.

PS: этот код не проверен. Не рекомендуется использовать его в производстве. Этот код предназначен только для примера. Может содержать ошибки.

...