Je suis loin d'être une autorité en la matière, et bien que la réponse ci-dessous ne corresponde pas exactement à votre problème, ma récente expérience de connexion sans proxylème à un service pourrait vous apporter quelques éclaircissements, à vous ou à la prochaine personne confrontée à un problème similaire.
Je commencerais par voir si vous pouvez lancer manuellement la requête SOAP à l'aide de fiddler, et voir si vous êtes en mesure de créer le bon message et de l'envoyer. Puisque vous décrivez les outils d'automatisation comme étant bogués (ou peut-être qu'il y a un problème de configuration que vous n'arrivez pas à résoudre). Quoi qu'il en soit, une bonne compréhension de la forme du contrat et la possibilité d'effectuer un test fiable avec fiddler peuvent apporter des éclaircissements.
Vous n'avez pas nécessairement besoin d'utiliser un proxy. Vous pouvez créer votre propre message de savon et l'envoyer de deux façons. La première consiste à utiliser ChannelFactory.
- Créez le corps de votre message (si nécessaire, la classe de message peut fonctionner sans corps).
- Créez votre message
- Envoyez votre message par l'intermédiaire de ChannelFactory
Pour l'étape 1, vous pouvez construire votre message en créant un simple POCO pour refléter ce qui est attendu dans votre contrat. Vous devriez être en mesure de dériver ce qu'est cette classe via le WSDL.
Supposons que le service se présente sous la forme suivante :
[ServiceContract(Namespace = "http://Foo.bar.car")]
public interface IPolicyService
{
[OperationContract]
PolicyResponse GetPolicyData(PolicyRequest request);
}
public class PolicyData : IPolicyService
{
public PolicyResponse GetPolicyData(PolicyRequest request)
{
var polNbr = request.REQ_POL_NBR;
return GetMyData(polNbr);
}
}
Vous auriez besoin d'une classe de ce type :
[DataContract(Namespace = "http://Foo.bar.car")]
public class GetPolicyData
{
[DataMember]
public request request { get; set; }
}
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy")]
public class request
{
///<summary>
/// Define request parameter for SOAP API to retrieve selective Policy level data
/// </summary>
[DataMember]
public string REQ_POL_NBR { get; set; }
}
et vous l'appelleriez ainsi :
private static Message SendMessage(string id)
{
var body = new GetPolicyData {request = new request{ REQ_POL_NBR = id }};
var message = Message.CreateMessage(MessageVersion.Soap11, "http://Foo.bar.car/IPolicyService/GetPolicyData", body);
// these headers would probably not be required, but added for completeness
message.Headers.Add(MessageHeader.CreateHeader("Accept-Header", string.Empty, "application/xml+"));
message.Headers.Add(MessageHeader.CreateHeader("Content-Type", string.Empty, "text/xml"));
message.Headers.Add(MessageHeader.CreateHeader("FromSender", string.Empty, "DispatchMessage"));
message.Headers.To = new System.Uri(@"http://localhost:5050/PolicyService.svc");
var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MessageEncoding = WSMessageEncoding.Text,
MaxReceivedMessageSize = int.MaxValue,
SendTimeout = new TimeSpan(1, 0, 0),
ReaderQuotas = { MaxStringContentLength = int.MaxValue, MaxArrayLength = int.MaxValue, MaxDepth = int.MaxValue }
};
message.Properties.Add("Content-Type", "text/xml; charset=utf-8");
message.Properties.Remove("Accept-Encoding");
message.Properties.Add("Accept-Header", "application/xml+");
var cf = new ChannelFactory<IRequestChannel>(binding, new EndpointAddress(new Uri("http://localhost:5050/PolicyService.svc")));
cf.Open();
var channel = cf.CreateChannel();
channel.Open();
var result = channel.Request(message);
channel.Close();
cf.Close();
return result;
}
Ce que vous recevrez en retour sera un message, que vous devrez désérialiser, et il y a quelques façons OOTB de le faire, (Message.GetReaderAtBodyContents, Message.GetBody) en accord avec le thème "hand-rolled" :
/// <summary>
/// Class MessageTransform.
/// </summary>
public static class MessageTransform
{
/// <summary>
/// Gets the envelope.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>XDocument.</returns>
public static XDocument GetEnvelope(Message message)
{
using (var memoryStream = new MemoryStream())
{
var messageBuffer = message.CreateBufferedCopy(int.MaxValue);
var xPathNavigator = messageBuffer.CreateNavigator();
var xmlWriter = XmlWriter.Create(memoryStream);
xPathNavigator.WriteSubtree(xmlWriter);
xmlWriter.Flush();
xmlWriter.Close();
memoryStream.Position = 0;
var xdoc = XDocument.Load(XmlReader.Create(memoryStream));
return xdoc;
}
}
/// <summary>
/// Gets the header.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>XNode.</returns>
public static XNode GetHeader(Message message)
{
var xdoc = GetEnvelope(message);
var strElms = xdoc.DescendantNodes();
var header = strElms.ElementAt(1);
return header;
}
/// <summary>
/// Gets the body.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="localName">Name of the local.</param>
/// <param name="namespaceName">Name of the namespace.</param>
/// <returns>IEnumerable<XElement>.</returns>
public static IEnumerable<XElement> GetBody(Message message, string localName, string namespaceName)
{
var xdoc = GetEnvelope(message);
var elements = xdoc.Descendants(XName.Get(localName, namespaceName));
return elements;
}
}
OU vous pouvez construire votre enveloppe de savon à la main et utiliser WebClient :
using System.Net;
using System.Xml.Linq;
public static class ClientHelper
{
public static string Post(string targetUrl, string action, string method, string key, string value)
{
var request = BuildEnvelope(method, key, value);
using (var webClient = new WebClient())
{
webClient.Headers.Add("Accept-Header", "application/xml+");
webClient.Headers.Add("Content-Type", "text/xml; charset=utf-8");
webClient.Headers.Add("SOAPAction", action);
var result = webClient.UploadString(targetUrl, "POST", request);
return result;
}
}
public static string BuildEnvelope(string method, string key, string value)
{
XNamespace s = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace d = "d4p1";
XNamespace tempUri = "http://tempuri.org/";
XNamespace ns = "http://Foo.bar.car";
XNamespace requestUri = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy";
var xDoc = new XDocument(
new XElement(
s + "Envelope",
new XAttribute(XNamespace.Xmlns + "s", s),
new XElement(
s + "Body",
new XElement(
ns + method,
new XElement(requestUri + "request",
new XElement(tempUri + key, value))
)
)
)
);
// hack - finish XDoc construction later
return xDoc.ToString().Replace("request xmlns=", "request xmlns:d4p1=").Replace(key, "d4p1:" + key);
}
qui est appelé avec :
return ClientHelper.Post("http://localhost:5050/PolicyService.svc", "http://Foo.bar.car/IPolicyService/GetPolicyData", "GetPolicyData", "REQ_POL_NBR", id);
Le tester dans Fiddler ressemblerait à quelque chose comme ceci :
Post action: http://localhost:5050/PolicyService.svc
Header:
User-Agent: Fiddler
SOAPAction: http://Foo.bar.car/IPolicyService/GetPolicyData
Content-type: text/xml
Host: localhost:5050
Content-Length: 381
Le corps :
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetPolicyData xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://Foo.bar.car">
<request xmlns:d4p1="http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy">
<d4p1:REQ_POL_NBR>1</d4p1:REQ_POL_NBR>
</request>
</GetPolicyData>
</s:Body>
</s:Envelope>
Encore une fois, cette réponse n'essaie pas de résoudre comment invoquer svcUtil différemment, mais d'éviter de l'appeler complètement, donc j'espère que les dieux de l'édition ne me puniront pas pour cela ;-)
Mon code ci-dessus a été inspiré par de meilleurs développeurs que moi, mais j'espère qu'il sera utile.
http://blogs.msdn.com/b/stcheng/archive/2009/02/21/wcf-how-to-inspect-and-modify-wcf-message-via-custom-messageinspector.aspx