Parece que a solução de Tim Carter não funciona se a chamada para a referência da web lançar uma exceção. Tenho tentado obter a ressonância da web bruta para que possa examiná-la (no código) no manipulador de erros, uma vez que a exceção é lançada. No entanto, estou descobrindo que o log de resposta escrito pelo método de Tim está em branco quando a chamada lança uma exceção. Não entendo completamente o código, mas parece que o método de Tim interrompe o processo após o ponto em que .Net já invalidou e descartou a resposta da web.
Estou trabalhando com um cliente que está desenvolvendo um serviço da web manualmente com codificação de baixo nível. Neste ponto, eles estão adicionando suas próprias mensagens de erro de processo interno como mensagens formatadas em HTML na resposta ANTES da resposta formatada em SOAP. Claro, a referência da web automagic .Net explode com isso. Se eu pudesse obter a resposta HTTP bruta após uma exceção ser lançada, eu poderia procurar e analisar qualquer resposta SOAP dentro da resposta HTTP de retorno mista e saber se eles receberam meus dados corretamente ou não.
Mais tarde ...
Esta é uma solução que funciona, mesmo após uma execução (observe que estou apenas após a resposta - também poderia obter a solicitação):
namespace ChuckBevitt
{
class GetRawResponseSoapExtension : SoapExtension
{
//must override these three methods
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type serviceType)
{
return null;
}
public override void Initialize(object initializer)
{
}
private bool IsResponse = false;
public override void ProcessMessage(SoapMessage message)
{
//Note that ProcessMessage gets called AFTER ChainStream.
//That's why I'm looking for AfterSerialize, rather than BeforeDeserialize
if (message.Stage == SoapMessageStage.AfterSerialize)
IsResponse = true;
else
IsResponse = false;
}
public override Stream ChainStream(Stream stream)
{
if (IsResponse)
{
StreamReader sr = new StreamReader(stream);
string response = sr.ReadToEnd();
sr.Close();
sr.Dispose();
File.WriteAllText(@"C:\test.txt", response);
byte[] ResponseBytes = Encoding.ASCII.GetBytes(response);
MemoryStream ms = new MemoryStream(ResponseBytes);
return ms;
}
else
return stream;
}
}
}
Veja como configurá-lo no arquivo de configuração:
<configuration>
...
<system.web>
<webServices>
<soapExtensionTypes>
<add type="ChuckBevitt.GetRawResponseSoapExtension, TestCallWebService"
priority="1" group="0" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
"TestCallWebService" deve ser substituído pelo nome da biblioteca (que por acaso era o nome do aplicativo de console de teste em que eu estava trabalhando).
Você realmente não deveria ter que ir para ChainStream; você deve ser capaz de fazer isso de forma mais simples a partir do ProcessMessage como:
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.BeforeDeserialize)
{
StreamReader sr = new StreamReader(message.Stream);
File.WriteAllText(@"C:\test.txt", sr.ReadToEnd());
message.Stream.Position = 0; //Will blow up 'cause type of stream ("ConnectStream") doesn't alow seek so can't reset position
}
}
Se você pesquisar SoapMessage.Stream, é suposto ser um fluxo somente leitura que você pode usar para inspecionar os dados neste ponto. Isso é uma bagunça, porque se você ler o fluxo, o processamento subsequente bombeará sem erros de dados encontrados (o fluxo estava no final) e você não pode redefinir a posição para o início.
Curiosamente, se você fizer os dois métodos, ChainStream e ProcessMessage, o método ProcessMessage funcionará porque você alterou o tipo de fluxo de ConnectStream para MemoryStream em ChainStream, e MemoryStream permite operações de busca. (Tentei transmitir o ConnectStream para MemoryStream - não foi permitido.)
Portanto ... a Microsoft deve permitir operações de busca no tipo ChainStream ou tornar o SoapMessage.Stream realmente uma cópia somente leitura, como deveria ser. (Escreva ao seu congressista, etc ...)
Mais um ponto. Depois de criar uma maneira de recuperar a resposta HTTP bruta após uma exceção, ainda não obtive a resposta completa (conforme determinado por um sniffer HTTP). Isso acontecia porque, quando o serviço da web de desenvolvimento incluía as mensagens de erro HTML no início da resposta, ele não ajustava o cabeçalho Content-Length, portanto, o valor Content-Length era menor que o tamanho do corpo da resposta real. Tudo o que consegui foi o número de caracteres do valor Content-Length - o resto estava faltando. Obviamente, quando .Net lê o fluxo de resposta, ele apenas lê o número de caracteres do Content-Length e não permite que o valor do Content-Length esteja possivelmente errado. É assim que deve ser; mas se o valor do cabeçalho Content-Length estiver errado, a única maneira de obter todo o corpo da resposta é com um sniffer HTTP (eu utilizo o HTTP Analyzer dohttp://www.ieinspector.com ).