9

Example:

public class Service1 : System.Web.Services.WebService
{
   [WebMethod]
   public int Add(int x, int y)
   {
       string request = getRawSOAPRequest();//How could you implement this part?
       //.. do something with complete soap request

       int sum = x + y;
       return sum;
   }
}
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Francisco Noriega
  • 13,725
  • 11
  • 47
  • 72

4 Answers4

11

An alternative to SoapExtensions is to implement IHttpModule and grab the input stream as it's coming in.

public class LogModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += this.OnBegin;
    }

    private void OnBegin(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;
        HttpContext context = app.Context;

        byte[] buffer = new byte[context.Request.InputStream.Length];
        context.Request.InputStream.Read(buffer, 0, buffer.Length);
        context.Request.InputStream.Position = 0;

        string soapMessage = Encoding.ASCII.GetString(buffer);

        // Do something with soapMessage
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }
}
nivlam
  • 3,223
  • 4
  • 30
  • 39
7

You can also read the contents of the Request.InputStream.

This way its more useful such as for cases when you want to perform validation or other actions within the WebMethod depending on the contents of the input.

using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.IO;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;

namespace SoapRequestEcho
{
  [WebService(
  Namespace = "http://soap.request.echo.com/",
  Name = "SoapRequestEcho")]
  public class EchoWebService : WebService
  {

    [WebMethod(Description = "Echo Soap Request")]
    public XmlDocument EchoSoapRequest(int input)
    {
      // Initialize soap request XML
      XmlDocument xmlSoapRequest = new XmlDocument();

      // Get raw request body
      Stream receiveStream = HttpContext.Current.Request.InputStream

      // Move to begining of input stream and read
      receiveStream.Position = 0;
      using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
      {
        // Load into XML document
        xmlSoapRequest.Load(readStream);
      }

      // Return
      return xmlSoapRequest;
    }
  }
}

NOTE: Updated to reflect Johns comment below.

Steven de Salas
  • 20,944
  • 9
  • 74
  • 82
  • Have you tried this code? Among other things, I don't think you should have `using` blocks, since you're not creating the `Stream`. The `StreamReader` also shouldn't be in a `using` block, since disposing it will also close the stream, and the stream doesn't belong to you. I also question whether this will interoperate with any `SoapExtension` that may later be configured. – John Saunders May 21 '11 at 02:45
  • Yep, I tried and works fine (try clicking on the link), as for using or not using, its just a programmer choice of how you want to structure your code. – Steven de Salas May 21 '11 at 18:44
  • 1
    @Steven: you're wrong about `using` in this case. If it was `using (var x = new StreamReader())` then it would be a choice - the `StreamReader` belongs to you because you created it. In this case, the `Stream` belongs to the `Request` object - you did not create it, so you should not be calling `Dispose` on it - ever. – John Saunders May 21 '11 at 21:22
  • @Steven: interesting. I would have thought that deserialization would already have read the stream to the end. Perhaps this isn't the original stream, but rather a new MemoryStream over the buffered contents of the request. It is well-known that ASMX services have a hard time with large requests because of the number of copies of the request that are kept in memory. Perhaps this is a stream over one of those copies. Otherwise, I would expect it to interfere with SoapExtensions, Serialization, or filters. – John Saunders May 21 '11 at 21:31
  • Sure, you may be right there with disposing the object, but at this point its unlikely anything will need to use the stream. – Steven de Salas May 21 '11 at 21:32
  • @Steven: care to be constructive? Are you saying it's ok to call Dispose on objects which "belong to" some other piece of code, or are you saying this object somehow "belongs" to your code despite the fact that you get it from a property of another object and don't create it yourself? – John Saunders May 21 '11 at 21:34
  • @Steven: best practice is to not depend on the current implementation of someone else's code (like the .NET Framework). You don't own that code, so are not in a position to know who is or is not likely to use that object again. Among other things, if this were the actual stream used to read from the network, then any SoapExtension or HttpModule might still want to read from it. – John Saunders May 21 '11 at 21:36
  • @Steven: just noticed your "all HttpModules have fired". **wrong**. Are there no post-request events left to be fired? Will a SoapExtension not be invoked _after_ the request has been processed? – John Saunders May 21 '11 at 21:37
  • @Steven: thanks for the edit, but I'd appreciate it if you would set a breakpoint before the end of the `using` block; check if the `Stream` is still open, then set one after the end and check if the `Stream` is still open. I'm concerned about the `StreamReader.Dispose` calling `Stream.Dispose`. – John Saunders May 21 '11 at 21:54
6

Yes, you can do it using SoapExtensions. Here's a nice article that runs through the process.

Eric Petroelje
  • 59,820
  • 9
  • 127
  • 177
1

I assume you are wanting to log the SOAP request for tracing; perhaps you have a consumer of your service that is telling you they're sending you good SOAP, but you don't believe them, yes?

In that case, you should (temporarily) enable trace logging on your service.

If you are trying to do general purpose logging, don't bother with the SOAP packet, since it's heavy; your logs would bloat up quick. Just log the important stuff, like e.g. "Add called, X=foo, Y=bar".

Randolpho
  • 55,384
  • 17
  • 145
  • 179
  • 1
    Unfortunately it needs to be permanent, so I cant enable the trace just temporarily. – Francisco Noriega Apr 06 '10 at 20:43
  • @bangoker: Although you've found a solution that fills your immediate need, I suggest you reconsider your course. Logging every SOAP request is heavy and extremely unnecessary. – Randolpho Apr 06 '10 at 20:47