2017-03-09 1 views
0

Dans le cadre de mon travail, je dois écrire un service Web SOAP 1.2 qui reçoit et traite les requêtes XML. Au cours des tests avec le client distant, cependant, j'ai rencontré un problème plutôt déconcertant et incohérent.La méthode de service SOAP C# reçoit un paramètre null au lieu de

C'est la signature de l'une des méthodes de services appelables:

[WebMethod] 
[ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Xml)] 
public XmlDocument PostOrders(XmlDocument request) 

Le problème est ici:

  • Lorsque la méthode est appelée à partir d'une application de test minimaliste je l'ai écrit, il travaille correctement.
  • Lorsque l'application php de mon client appelle la méthode, la méthode passe un request nul et, par conséquent, lance un NullReferenceException sur la ligne lorsque l'application tente d'écrire son contenu dans le fichier. Peu importe ce que le client a effectivement envoyé, attacher Visual Studio au processus IIS révèle que la méthode est appelée avec un request nul, avec ce que le client a réellement envoyé ayant été perdu quelque part au niveau .NET.

Ce que j'essayé:

  • tests indépendants. This produit le même problème - mais la même requête copypgée dans SoapUI est correctement reçue. C'est pourquoi j'ai indiqué plus haut que ce problème est incohérent: deux applications sur quatre, chacune sur des ordinateurs différents, reproduisent le problème, les deux autres ne le font pas.
  • Apparemment, cela peut arriver si le serveur attend SOAP 1.2 mais reçoit 1.1, et j'ai été capable de le reproduire en postant intentionnellement la requête en tant que 1.1 dans SoapUI. J'ai donc désactivé à la fois le 1.1 et le HTTP de base dans web.config (en vérifiant que les deux avaient disparu de WSDL, ne laissant que le 1.2) et que le client définissait explicitement la version 1.2 de leur côté. Pas de dé.
  • Après modification du service, la définition WSDL côté client de la méthode est passée brusquement de XmlDocument à Linq XElement. Soupçonnant un problème de désérialisation côté serveur dû à la découverte que WSDL abstrait les types de données attendus pour la compatibilité multiplateforme, j'ai changé le paramètre de la méthode de XmlDocument à XElement. Pas de dé.

Contenu de web.config:

<configuration> 
    <system.web> 
    <compilation debug="true" targetFramework="4.5.2" /> 
    <httpRuntime /> 
    <webServices> 
     <protocols> 
     <remove name="HttpGet" /> 
     <remove name="HttpPost" /> 
     <remove name="HttpSoap"/>  <!-- disables SOAP 1.1 --> 
     </protocols> 
     <conformanceWarnings> 
     <remove name='BasicProfile1_1'/> 
     </conformanceWarnings> 
    </webServices> 
    <globalization uiCulture="en-US" /> 
    <customErrors mode="Off" /> 
    <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" /> 
    <httpModules> 
     <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" /> 
    </httpModules> 
    </system.web> 
    <system.codedom> 
    <compilers> 
     <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> 
      <providerOption name="CompilerVersion" value="v4.0" /> 
     </compiler> 
     <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" /> 
    </compilers> 
    </system.codedom> 
    <system.webServer> 
    <modules> 
     <remove name="ApplicationInsightsWebTracking" /> 
     <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" /> 
    </modules> 
    <handlers> 
     <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> 
     <remove name="OPTIONSVerbHandler" /> 
     <remove name="TRACEVerbHandler" /> 
     <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> 
    </handlers> 
    <validation validateIntegratedModeConfiguration="false" /> 
    </system.webServer> 
    <system.serviceModel> 
    <bindings /> 
    <client /> 
    </system.serviceModel> 
    <runtime> 
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
     <dependentAssembly> 
     <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> 
     <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" /> 
     </dependentAssembly> 
    </assemblyBinding> 
    </runtime> 
</configuration> 

Considérant que j'ai pu envoyer avec succès la demande de deux ordinateurs distincts dans deux applications distinctes, sans aucune configuration manuelle, mais pas d'un autre deux, est-ce un server- problème secondaire ou une mauvaise configuration côté client?

Répondre

0

J'ai trouvé la solution et je la poste au cas où quelqu'un d'autre rencontrerait ce problème.

Il s'agissait d'une erreur côté serveur. Si vous regardez la signature de la méthode que je posté ci-dessus:

public XmlDocument PostOrders(XmlDocument request) 

Le problème était le paramètre étant un XmlDocument. Comme la charge utile d'une requête SOAP est déjà contenue dans un fichier XML, elle ne peut pas être désérialisée en tant que XmlDocument car l'élément racine de la charge utile n'est pas un élément racine d'où elle vient, seulement XmlElement ou Linq XElement.Pourtant, même changer le type de paramètre n'a pas fonctionné - et c'est là que j'ai regardé le WSDL. Il s'est avéré que ni les types XML .NET ne fonctionnaient car même lorsque j'ai choisi une classe sérialisable (XmlElement/XElement), tout ce qui génère le WSDL ne les reconnaissait pas et ne pouvait pas déterminer quel type de données la méthode est Attendant, le WSDL n'a pas déclaré le type de données du paramètre. En l'absence de type de données déclaré dans le WSDL, l'application côté client ne savait pas non plus quoi envoyer, donc elle découpait la charge utile et envoyait uniquement l'en-tête SOAP. Je ne sais pas pourquoi les classes SOAP de php fonctionnent comme ça, mais apparemment elles le font. Je ne l'ai pas remarqué pendant le test car mon application de test est également .NET et sait donc automatiquement quoi envoyer, alors que les requêtes de SoapUI sont des chaînes brutes sans sérialisation avant l'envoi, donc elle n'a pas besoin du type de données.

La solution était de changer le type de paramètre en string et de désactiver le HTTP de base. Alors que le HTTP de base était activé, la désérialisation lançait toujours une exception lorsque la méthode était appelée avec un paramètre de chaîne, d'où la raison pour laquelle je n'ai pas trouvé cette solution plus tôt.

Conclusion: si vous écrivez un service Web .NET non exclusivement pour les clients .NET, n'utilisez pas les classes .NET comme paramètres d'entrée, qu'ils soient sérialisables ou non. Utilisez des chaînes. Vous économiserez beaucoup de maux de tête.