Avatar billede luctatic Nybegynder
15. november 2007 - 15:50 Der er 24 kommentarer

Gem soap request til webservice

Jeg har en webservice hvor der bliver foretaget nogle soap kald. Jeg ville gerne logge disse kald i sql, så jeg kan se hvad i vores kunders soap kald der er forkert og nogle gange får soap kaldene til at fejle.

Men jeg kan ikke helt lure, hvordan jeg kan få fat på hele soap envelopen ovre i webservicen, da det jo som sådan er den jeg vil have fat i - og også logge.

Hvordan klarer man lige den?
Avatar billede arne_v Ekspert
16. november 2007 - 05:17 #1
Jeg tror at du skal lave et HTTP Module ligesom i http://www.eggheadcafe.com/articles/20030813.asp !

Jeg kan evt. prøve at bixe noget simpelt C# kode i weekenden.
Avatar billede luctatic Nybegynder
16. november 2007 - 08:55 #2
Hmm, så man bliver ligefrem nød til at lave en streamreader for at læse httpstreamen, og så hive tingene ud derfra?

Jeg regnede faktisk med at man kunne læse SOAP-kaldet relativt let, på en eller anden måde. Evt et SOAP objekt, hvori alle informationer lå, som FX den XML som SOAP'en indeholder.
Avatar billede arne_v Ekspert
19. november 2007 - 02:44 #3
Lidt mere tricky end som så.

Her er noget kode.
Avatar billede arne_v Ekspert
19. november 2007 - 02:44 #4
web.config fragment:

<configuration>
    ...
    <system.web>
        ...
        <httpModules>
            <add name="RequestLoggerModule" type="RequestLoggerModule" />
        </httpModules>
    </system.web>
</configuration>
Avatar billede arne_v Ekspert
19. november 2007 - 02:45 #5
HTTP module:

using System;
using System.IO;
using System.Web;

public class RequestLoggerModule : IHttpModule
{
    private static StreamWriter log;
    public void Init(HttpApplication app)
    {
        app.BeginRequest += OnBeginRequest;
        app.EndRequest += OnEndRequest;
    }
    protected void OnBeginRequest(object sender, EventArgs e)
    {
        log = new StreamWriter(@"C:\inetpub\wwwroot\z.z", true);
        HttpRequest req = ((HttpApplication)sender).Request;
        log.WriteLine(req.HttpMethod + " " + req.Url);
        foreach(string hdr in req.Headers.AllKeys)
        {
            log.WriteLine(hdr + ": " + req.Headers[hdr]);
        }
        log.WriteLine();
        req.Filter = new RequestLogger(req.Filter, log);
    }
    protected void OnEndRequest(object sender, EventArgs e)
    {
        log.WriteLine();
        log.WriteLine("========");
        log.Close();
    }
    public void Dispose()
    {
    }
}
Avatar billede arne_v Ekspert
19. november 2007 - 02:45 #6
Det filter vi bruger:

using System;
using System.IO;
using System.Text;

public class RequestLogger : Stream
{
    private Stream real;
    private StreamWriter log;
    public RequestLogger(Stream real, StreamWriter log)
    {
        this.real = real;
        this.log = log;
    }
    public override bool CanRead
    {
        get
        {
            return true;
        }
    }
    public override bool CanSeek
    {
        get
        {
            return false;
        }
    }
    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }
    public override long Length
    {
        get
        {
            return real.Length;
        }
    }
    public override long Position
    {
        get
        {
            return real.Position;
        }
        set
        {
            throw new NotSupportedException();
        }
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        int n = real.Read(buffer, offset, count);
        log.Write(Encoding.UTF8.GetString(buffer, offset, n));
        log.Flush();
        return n;
    }
    public override long Seek(long offset, SeekOrigin direction)
    {
        throw new NotSupportedException();
    }
    public override void SetLength(long length)
    {
        throw new NotSupportedException();
    }
    public override void Close()
    {
        real.Close();
    }
    public override void Flush()
    {
        real.Flush();
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }
}
Avatar billede arne_v Ekspert
19. november 2007 - 04:41 #7
Den er naturligvis aldeles thread unsafe.

Det må du selv finde en løsning på hvis det er et problem.
Avatar billede luctatic Nybegynder
19. november 2007 - 18:52 #8
Så dvs at al den information jeg ønsker at hive ud fra SOAP objektet, ligger i Headers.AllKeys?
Avatar billede arne_v Ekspert
19. november 2007 - 18:58 #9
Nej.

Det er kun HTTP headerne.

Det er:

req.Filter = new RequestLogger(req.Filter, log);

og:

    public override int Read(byte[] buffer, int offset, int count)
    {
        int n = real.Read(buffer, offset, count);
        log.Write(Encoding.UTF8.GetString(buffer, offset, n));
        log.Flush();
        return n;
    }

som skrive al SOAP XML ud.

(jeg ved ikke engang om du vil have HTTP headerne, men jeg synes at de hoerte med)
Avatar billede luctatic Nybegynder
19. november 2007 - 20:16 #10
Ok. Jamen det prøver jeg så når jeg er tilbage på arbejdet på onsdag.
Avatar billede luctatic Nybegynder
21. november 2007 - 16:30 #11
Ok, så har jeg prøvet det, men z.z bliver en tom fil uden indhold.

En testwebservice:
[WebMethod(Description="Test")]
public string fisk(string f)
{
    try
    {
        StreamWriter log;
        log = new StreamWriter(@"C:\inetpub\wwwroot\webservices\z.txt", true);
        System.Web.HttpContext.Current.Request.Filter = new Webservices.HttpFilter(System.Web.HttpContext.Current.Request.Filter, log);
        System.IO.StreamReader reader = new StreamReader(System.Web.HttpContext.Current.Request.InputStream);

        return "ok";
    }
    catch(Exception ex)
    {
        return ex.ToString();
    }
}


Dette kalder jeg fra en win-app som bare kalder metoden, og skriver resultatet ud til en label. Den skriver 'ok' og danner filen z.txt, men der står som sagt intet i den.
Avatar billede arne_v Ekspert
21. november 2007 - 17:06 #12
Din kode har da intet med mit forslag at goere.

Du skal ikke aendre noget som helst i din web service.

Du skal lave et HTTP module som logger requests.

Og den logning sker inden din web service metode bliver kaldt.
Avatar billede luctatic Nybegynder
21. november 2007 - 18:26 #13
Jeg satsede bare på at man kunne gøre det samme indefra selve webservicen, så man, som jeg nævnte, kun loggede på de metoder man ønsker.

Skriver jeg det httpmodul du nævner, så bliver samtlige webservices jo loggede.
Avatar billede arne_v Ekspert
21. november 2007 - 18:28 #14
du kan teste paa req.Url og kun saette filter hvis det er den rigtige

raekkefoelgen er:
  containeren laeser XML request
  kalder den rigtige web service med de rigtige parametre udfra XML request
og derfor er det fo sent at filtere naar man er i web servicen
Avatar billede luctatic Nybegynder
21. november 2007 - 18:34 #15
Ah ok. Jeg prøvede også at se om man evt. kunne læse det fra Request.InputStream, men der var heller ingen gevinst. Jeg må prøve at smide det ind som et httpModul og filtrere imorgen så.
Avatar billede luctatic Nybegynder
22. november 2007 - 12:52 #16
Ok, så fik jeg det prøvet af. Jeg lavede to nye classes (RequestLogger og RequestLoggerModule), lagde din kode ind, og skrev modulet ind i web.config, men....

Den nægter at compile med følgende info:
Method 'Webservices.RequestLoggerModule.OnBeginRequest(object, System.EventArgs)' referenced without parentheses
Method 'Webservices.RequestLoggerModule.OnEndRequest(object, System.EventArgs)' referenced without parentheses


Dette henviser til disse linier:
app.BeginRequest += OnBeginRequest;
app.EndRequest += OnEndRequest;


Projektet kører 1.1, hvilket måske kan være en årsag?
Avatar billede arne_v Ekspert
22. november 2007 - 16:34 #17
Uff - jeg havde ikke lige tænkt på 1.1 (3.5 er trods alt lige releaset).

Jeg prøver lige at finde ud af hvordan man gør det i 1.1 !
Avatar billede luctatic Nybegynder
22. november 2007 - 17:33 #18
Hehe, ja jeg lurede på om det var grunden. Vi har endnu ikke opgraderet til 2.0, og vi regner med at springe den over og smutte direkte til 3.5. Jeg savner dog et par ting i 2.0, og nu savner jeg så også dette =)
Avatar billede arne_v Ekspert
23. november 2007 - 05:24 #19
prøv at rette:

app.BeginRequest += OnBeginRequest;
app.EndRequest += OnEndRequest;

til:

app.BeginRequest += new EventHandler(OnBeginRequest);
app.EndRequest += new EventHandler(OnEndRequest);
Avatar billede luctatic Nybegynder
23. november 2007 - 10:59 #20
Ok, nu vil den compile, men åbner jeg asmx filen får jeg følgende:

Parser Error Message: Could not load type RequestLoggerModule from assembly System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.

RequestLogger og RequestLoggerModule ligger begge i rodbiblioteket, og følgende ligger i system.web i web.config:
    <httpModules>
        <add name="RequestLoggerModule" type="RequestLoggerModule" />
    </httpModules>


Hvis jeg prøver at lave et SOAP kalde via min win-app, får jeg følgende fejl:
System.InvalidOperationException: Client found response content type of 'text/html; charset=utf-8', but expected 'text/xml'.

Så det er jo underligt nok to forskellige fejl tilsyneladende.
Avatar billede arne_v Ekspert
24. november 2007 - 02:04 #21
Hvis det er .NET 1.1, så skal DLL filerne ligge i bin dir og ikke i roden.
Avatar billede arne_v Ekspert
24. november 2007 - 02:05 #22
(det skal de strengt taget også i 2.0, men der kan du alternativt smide sourcen i
app_code dir)
Avatar billede luctatic Nybegynder
24. november 2007 - 10:31 #23
Hmm, ok, så jeg skal compiler requestlogger og requestloggermodule i et andet projekt, og smide deres dll'er i bin som en reference. Ok, det prøver jeg så.
Avatar billede arne_v Ekspert
25. februar 2008 - 03:24 #24
Alt OK ?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester