Получите SOAP-сообщение перед отправкой в WebService в.NET
Я звоню на внешний HTTPS веб-сервис.
Чтобы проверить, что не так, владельцу нужен SOAP-запрос, который я отправляю.
У меня есть веб-ссылка и сгенерированный прокси-класс, созданный VS 2008...
Есть ли способ увидеть сообщение SOAP перед его отправкой?
Я думаю, в некотором.net-коде... потому что Sniffers, которые я пробовал, не "видели" вызов веб-службы, не знаю почему.
4 ответа
Что вам нужно, так это SoapExtension. Здесь довольно много хороших примеров:
Как получить доступ к ответу SOAP
Получение данных RAW-мыла из Web-клиента, работающего на ASP.net
Ошибка синтаксического анализа XML при обработке ответа SOAP
Одна из статей связана с: http://msdn.microsoft.com/en-us/magazine/cc164007.aspx
Также выполните поиск ТА для: https://stackru.com/search?q=SoapExtension
Вы можете просто сериализовать объект запроса перед отправкой, например так:
var sreq = new SomeSoapRequest();
// ... fill in here ...
var serxml = new System.Xml.Serialization.XmlSerializer(sreq.GetType());
var ms = new MemoryStream();
serxml.Serialize(ms, sreq);
string xml = Encoding.UTF8.GetString(ms.ToArray());
// in xml string you have SOAP request
Предложение @mting923 было действительно полезным, спасибо!
Другие идеи (например, использование SoapExtension или создание "шпионских" классов а-ля Как мне получить доступ к ответу SOAP) интересны, но не могут быть использованы в.NET Core. Но сгенерированный прокси-класс SOAP по-прежнему остается просто клиентом WCF под капотом, и поэтому подход IClientMessageInspector работает хорошо, даже для функции.NET Core Azure, вызывающей старую веб-службу SOAP.
В приведенном выше примере ответная проводная связь подразумевается, но не показана полностью. Кроме того, в последних версиях API.NET CoreMessageInspectors
сейчас ClientMessageInspectors
а также Behaviors
сейчас EndpointBehaviors
. Итак, для полноты, в функции Azure.NET Core 3.1 у меня работает следующее:
public class SoapMessageInspector : IClientMessageInspector
{
public string LastRequestXml { get; private set; }
public string LastResponseXml { get; private set; }
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
LastRequestXml = request.ToString();
return request;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
LastResponseXml = reply.ToString();
}
}
public class SoapInspectorBehavior : IEndpointBehavior
{
private readonly SoapMessageInspector inspector_ = new SoapMessageInspector();
public string LastRequestXml => inspector_.LastRequestXml;
public string LastResponseXml => inspector_.LastResponseXml;
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(inspector_);
}
}
И тогда это можно настроить так:
var client = new ServiceClient();
var soapInspector = new SoapInspectorBehavior();
client.Endpoint.EndpointBehaviors.Add(soapInspector);
После вызова веб-службы на прокси-сервере клиента, soapInspector.LastRequestXml
а также soapInspector.LastResponseXml
будет содержать необработанный запрос и ответ SOAP (в виде строк).
Вы можете использовать IClientMEssageInspector и IEndpointBehavior, чтобы выполнить это. Я обнаружил, что использование этого способа может захватить точный запрос мыла, скорее всего, как запрос скрипача:
Создайте такой класс в том же проекте:
public class ClientMessageInspector : System.ServiceModel.Dispatcher.IClientMessageInspector
{
#region IClientMessageInspector Members
public string LastRequestXml { get; private set; }
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
string requestHeaderName = request.Headers.Action.Replace("urn:#",string.Empty);
LastRequestXml = request.ToString();
string serializedRequestFile = string.Format(requestHeaderName + "_request_{0}.xml", DateTime.Now.ToString("yyyyMMddHHmmss"));
string exportedFolder = ConfigurationManager.AppSettings["SubmittedRequestXmLocation"];
printSoapRequest(request, exportedFolder, serializedRequestFile);
return request;
}
public void printSoapRequest(System.ServiceModel.Channels.Message request, string exportedFolder, string fileName)
{
if (exportedFolder.Equals(string.Empty))
return;
if (!Directory.Exists(exportedFolder))
{
Directory.CreateDirectory(exportedFolder);
}
string exportedFile = string.Format("{0}\\{1}", exportedFolder, fileName);
if (File.Exists(exportedFile))
{
File.Delete(exportedFile);
}
string strRequestXML = request.ToString();
XDocument xDoc = XDocument.Parse(strRequestXML);
XmlWriter xw = XmlWriter.Create(exportedFile);
xDoc.Save(xw);
xw.Flush();
xw.Close();
LogOutput("Request file exported: " + exportedFile);
}
}
public class CustomInspectorBehavior : IEndpointBehavior
{
private readonly ClientMessageInspector clientMessageInspector = new ClientMessageInspector();
public string LastRequestXml
{
get { return clientMessageInspector.LastRequestXml; }
}
public string LastResponseXml
{
get { return clientMessageInspector.LastRequestXml; }
}
public void AddBindingParameters(
ServiceEndpoint endpoint,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(clientMessageInspector);
}
}
Тогда вы можете назвать это следующим образом:
ProxyClass _class = new ProxyClass();
var requestInterceptor = new CustomInspectorBehavior();
_client.Endpoint.Behaviors.Add(requestInterceptor);
Когда вы вызываете сервисный метод, он автоматически выполняет перехватчик и печатает вывод. Используя этот способ, вы также можете манипулировать мыльным сообщением перед отправкой на сервер!
Добавьте это в элемент вашего файла web.config или App.config. Он создаст файл trace.log в папке bin/Debug вашего проекта. Или вы можете указать абсолютный путь к файлу журнала с помощью атрибута initializeData.
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="System.Net" maxdatasize="9999" tracemode="protocolonly">
<listeners>
<add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
</switches>
</system.diagnostics>
Он предупреждает, что атрибуты maxdatasize и tracemode не разрешены, но они увеличивают объем данных, которые могут быть зарегистрированы, и избегают регистрации всего в шестнадцатеричном формате.
Если вы работаете в более ограниченной среде и не можете позволить себе такое приложение, как Fiddler, вы можете сделать следующее:
- Создайте свою веб-ссылку как обычно.
- Напишите код для выполнения любого вызова веб-метода, который вы собираетесь мне.
- Создайте новый ASP .NET проект по вашему выбору, я пошел с MVC 4.
- Создайте обработчик или контроллер / действие и извлеките поток запросов следующим образом:
using (var reader = new System.IO.StreamReader(Request.InputStream))
{
result = reader.ReadToEnd();
}
- Установите точку останова и запустите ее в режиме отладки.
- На вашем клиенте установите URL-адрес SOAP-запроса для вашего нового контроллера / обработчика.
- Запустите ваш клиент. Вы должны поймать точку останова в своем веб-приложении с помощью сообщения SOAP.
Это не идеальное или симпатичное решение, но если вы работаете в умеренно ограниченных условиях, оно выполнит свою работу.