Change Soap XML Request prior sending

2.1k Views Asked by At

I'm new to this soap message and stuff, I'm having some troubles using a WebService, I created a Class using WSDL.exe, the first issue I had was when the command was executed, it created a parameter with var[][] which I had to replace for a single var[] (array).

Then it was successfully compiling, now I having some issues and I have no idea how to fix it:

The request I'm sending is this one:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <emitirWS xmlns="http://neon.stoconsulting.com/NeonEmisionWS/NeonEmisionWS?wsdl">
            <comprobante serie="W" folio="143" formaPago="01" condicionesDePago="CONTADO" tipoCambio="1.000000" moneda="MXN" metodoPago="PUE" lugarExpedicion="54080" tipoComprobante="I" subTotal="827.586207" descuento="0.000000" total="960.00" documentoErp="143" usoCfdi="G03" tipoDocumento="1">
                <envioCfdi enviarXml="1" enviarPdf="1" enviarZip="0" emails="[email protected]" />
                <emisor rfc="ASM160607Q43" nombre="ARTHUR AND SONS MEXICO" regimenFiscal="601" idEmisorSto="1" idEmisorErp="1" />
                <sucursal rfc="ASM160607Q43" nombre="Plaza Polanco" regimenFiscal="601" idEmisorSto="2" idEmisorErp="2" numeroExterior="411" calle="Avenida Palmas" colonia="Miguel Hidalgo" referencia="" municipio="Toluca" estado="Edo. de México" pais="Mexico" codigoPostal="54080" email="[email protected]" idTipoEmisor="2" idEmisorPadre="1" estatusRegistro="1" />
                <receptor rfc="PIVF810724LW9" nombre="Felipe Pina Vera" regimenFiscal="622" usoCfdi="G01" idReceptoSto="1" idReceptorErp="1" numeroExterior="141A" calle="Viveros de Coyoacán" colonia="Viveros de la Loma" municipio="Tlalnepantla" estado="Edo. de México" pais="Mexico" codigoPostal="54080" email="[email protected]" idEmisorPadre="0" estatusRegistro="1" />
                <conceptos claveProdServ="42142901" cantidad="1.000000" claveUnidad="H87" unidad="Pieza" numIdentificacion="5" descripcion="BENSEN - AZUL" valorUnitario="827.586207" importe="827.586207" descuento="0.000000" xmlns="">
                    <impuestos>
                        <trasladados base="827.586207" impuesto="002" tipoFactor="Tasa" tasaOCuota="0.160000" importe="132.413793" />
                    </impuestos>
                </conceptos>
                <impuestos totalImpuestosRetenidos="0.000000" totalImpuestosTrasladados="132.413793" xmlns="">
                    <trasladados impuesto="002" tipoFactor="Tasa" tasaOCuota="0.160000" importe="132.413793" />
                </impuestos>
            </comprobante>
        </emitirWS>
    </soap:Body>
</soap:Envelope>

When it reaches to the server, it cannot be processed since the request doesn't have the right format, the right format is this one:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:neon="http://neon.stoconsulting.com/NeonEmisionWS/NeonEmisionWS?wsdl">
    <soap:Body>
        <neon:emitirWS>
            <comprobante serie="W" folio="143" formaPago="01" condicionesDePago="CONTADO" tipoCambio="1.000000" moneda="MXN" metodoPago="PUE" lugarExpedicion="54080" tipoComprobante="I" subTotal="827.586207" descuento="0.000000" total="960.00" documentoErp="143" usoCfdi="G03" tipoDocumento="1">
                <envioCfdi enviarXml="1" enviarPdf="1" enviarZip="0" emails="[email protected]" />
                <emisor rfc="ASM160607Q43" nombre="ARTHUR AND SONS MEXICO" regimenFiscal="601" idEmisorSto="1" idEmisorErp="1" />
                <sucursal rfc="ASM160607Q43" nombre="Plaza Polanco" regimenFiscal="601" idEmisorSto="2" idEmisorErp="2" numeroExterior="411" calle="Avenida Palmas" colonia="Miguel Hidalgo" referencia="" municipio="Toluca" estado="Edo. de México" pais="Mexico" codigoPostal="54080" email="[email protected]" idTipoEmisor="2" idEmisorPadre="1" estatusRegistro="1" />
                <receptor rfc="PIVF810724LW9" nombre="Felipe Pina Vera" regimenFiscal="622" usoCfdi="G01" idReceptoSto="1" idReceptorErp="1" numeroExterior="141A" calle="Viveros de Coyoacán" colonia="Viveros de la Loma" municipio="Tlalnepantla" estado="Edo. de México" pais="Mexico" codigoPostal="54080" email="[email protected]" idEmisorPadre="0" estatusRegistro="1" />
                <conceptos claveProdServ="42142901" cantidad="1.000000" claveUnidad="H87" unidad="Pieza" numIdentificacion="5" descripcion="BENSEN - AZUL" valorUnitario="827.586207" importe="827.586207" descuento="0.000000">
                    <impuestos>
                        <trasladados base="827.586207" impuesto="002" tipoFactor="Tasa" tasaOCuota="0.160000" importe="132.413793" />
                    </impuestos>
                </conceptos>
                <impuestos totalImpuestosRetenidos="0.000000" totalImpuestosTrasladados="132.413793">
                    <trasladados impuesto="002" tipoFactor="Tasa" tasaOCuota="0.160000" importe="132.413793" />
                </impuestos>
            </comprobante>
        </neon:emitirWS>
    </soap:Body>
</soap:Envelope>

I need to switch:

  1. in envelope node, I need to add a new attribute:

    xmlns:neon="http://neon.stoconsulting.com/NeonEmisionWS/NeonEmisionWS?wsdl"

  2. on emitir node, I need it needs to be <neon:emitir></neon:emitir> instead of the regular <emitir>

When I do that everything works like a charm, like I said I created the class with the wsdl.exe tool, this is the code I'm using:

 [System.Web.Services.Protocols.SoapDocumentMethodAttribute("", RequestNamespace = "http://neon.stoconsulting.com/NeonEmisionWS/NeonEmisionWS?wsdl", ResponseNamespace = "http://neon.stoconsulting.com/NeonEmisionWS/NeonEmisionWS?wsdl", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
        [return: System.Xml.Serialization.XmlElementAttribute("RespuestaWS", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
        [WebServiceSOAPExtension]
        public respuestaWS emitirWS(comprobante comprobante)
        {
            object[] results = this.Invoke("emitirWS", new object[] {
                    comprobante});
            return ((respuestaWS)(results[0]));
        }

Thanks, I have no idea where to begin.

1

There are 1 best solutions below

0
On

Well for all the guys over there who are triying to do something like this and you have no idea, i ended up doing some nasty stuff that i don't recommed, however if you are stuck like me and you have no clue where to begin to do it the "right" way, well you can do this:

First, you need to create a class that extends SoapExtension Like this one:

namespace SoapExentender
{
    class WebServiceSOAPExtension : SoapExtension
    {
        Stream oldStream;
        Stream newStream;
        string filename;

    public override Stream ChainStream(Stream stream)
    {
        oldStream = stream;
        newStream = new MemoryStream();
        return newStream;
    }

    // When the SOAP extension is accessed for the first time, the XML Web service method it is applied to is accessed to store the file name passed in,

    //using the corresponding SoapExtensionAttribute.    
    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
    {
        return null;
    }

    // The SOAP extension was configured to run using a configuration file instead of an attribute applied to a specific XML Web service method.
    public override object GetInitializer(Type WebServiceType)
    {
        return null;
    }

    // Receive the file name stored by GetInitializer and store it in a member variable for this specific instance.
    public override void Initialize(object initializer)
    {
        filename = ConfigurationManager.AppSettings["STOFacturacionDLL"].ToString();
    }

    //  If the SoapMessageStage is such that the SoapRequest or SoapResponse is still in the SOAP format to be sent or received, save it out to a file.
    public override void ProcessMessage(SoapMessage message)
    {
        switch (message.Stage)
        {
            case SoapMessageStage.BeforeSerialize:
                break;
            case SoapMessageStage.AfterSerialize: //el xml serializado de salida
                WriteOutput((SoapClientMessage)message);
                break;
            case SoapMessageStage.BeforeDeserialize:
                WriteInput((SoapClientMessage)message);
                break;
            case SoapMessageStage.AfterDeserialize:
                break;
            default:
                throw new Exception("Stage inválido");
        }
    }

    // Write the contents of the outgoing SOAP message to the log file.
    public void WriteOutput(SoapClientMessage message)
    {
        newStream.Position = 0;
        FileStream fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
        StreamWriter myStreamWriter = new StreamWriter(fs);
        myStreamWriter.WriteLine("================================== Fecha del request: " + DateTime.Now);
        // Print to the log file the request header field for SoapAction header.
        myStreamWriter.WriteLine(@"La acción SOAP del 'header' Http request  es: " + message.Action);

        // Print to the log file the method invoked by the client.
        myStreamWriter.WriteLine("El metodo llamado fue: " + message.MethodInfo.Name);

        // Print to the log file if the method invoked is OneWay.
        if (message.OneWay)
            myStreamWriter.WriteLine("El cliente no espera a que se termine el proceso (no es de un solo sentido)");
        else
            myStreamWriter.WriteLine("El cliente espera a que se termine el proceso");

        // Print to the log file the URL of the site that provides implementation of the method.
        myStreamWriter.WriteLine("La URL solicitada fue: " + message.Url);
        myStreamWriter.WriteLine("el contenido del request/response del ---- <soap:envelope> ---- es : ");
        myStreamWriter.Flush();

        Copy(newStream, fs);
        myStreamWriter.Close();
        //replace custom text
        string stringRequest = "";
        try
        {
            newStream.Position = 0;
            MemoryStream RequestStream = new MemoryStream();
            Copy(newStream, RequestStream);
            RequestStream.Position = 0;
            byte[] bytesRequestStream = ReadFully(RequestStream);
            stringRequest = System.Text.Encoding.UTF8.GetString(bytesRequestStream);
            stringRequest = replaceText(stringRequest, "<ReimprimirReferencia", "<thepanch:ReimprimirReferencia");
            int i = 0;
        }
        catch (Exception exc) { }

        newStream.Position = 0;
        //replace the original stream, with the custom stream
        Copy(ConvertStringToStream(stringRequest), oldStream);
    }

    private string replaceText(string MainString, string SearchString, string ReplaceWith)
    {
        MainString = MainString.Replace(SearchString, ReplaceWith);
        return MainString;
    }

    private MemoryStream ConvertStringToStream(string RequestString)
    {
        byte[] byteArray = Encoding.UTF8.GetBytes(RequestString);
        MemoryStream stream = new MemoryStream(byteArray);
        stream.Position = 0;
        return stream;
    }

    private byte[] ReadFully(Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }

    public void WriteInput(SoapMessage message)
    {
        Copy(oldStream, newStream);
        FileStream fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
        StreamWriter w = new StreamWriter(fs);
        string soapString = (message is SoapServerMessage) ? "SoapRequest" : "SoapResponse";
        w.WriteLine("----------" + soapString + " at " + DateTime.Now);
        w.Flush();
        newStream.Position = 0;
        Copy(newStream, fs);
        w.Close();
        newStream.Position = 0;
    }

    void Copy(Stream from, Stream to)
    {
        TextReader reader = new StreamReader(from);
        TextWriter writer = new StreamWriter(to);
        writer.WriteLine(reader.ReadToEnd());
        writer.Flush();
    }
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class WebServiceSOAPExtensionAttribute : SoapExtensionAttribute
{
    private int priority;

    public override Type ExtensionType
    {
        get { return typeof(WebServiceSOAPExtension); }
    }

    public override int Priority
    {
        get { return priority; }
        set { priority = value; }
    }
}

}

And then use a custom attribute on the proxy's method to replace the actual Stream with your custom stream, the writeOutput method is for the "request" and the writeInput method is for the "response", then you need to play with your strings to fit your needs, i hope it helps.

As i mentioned on the first lines, this is not the right way to it, however if you are like me with no clue where to begin, you can write your custom Xml string.