Come faccio a modificare WCF per elaborare i messaggi in un formato diverso (non SOAP)?

StackOverflow https://stackoverflow.com/questions/4437660

  •  09-10-2019
  •  | 
  •  

Domanda

Sto lavorando con WCF per scambiare messaggi con una società di terze parti. I messaggi devono essere inviati e ricevuti in una busta che corrisponde al ebXML specifica . Idealmente mi piacerebbe utilizzare come gran parte dello stack WCF possibile ed evitare il un metodo per elaborare tutti loro approccio come in questo caso ciò significherebbe scrivere gran parte delle infrastrutture di WCF di nuovo.

Per quanto posso vedere dalla mia ricerca iniziale questo mi avrebbe bisogno di scrivere il mio legame personalizzato ma sto lottando per trovare chiarezza nella documentazione in MSDN.

Sono stato in grado di trovare un sacco di documenti dettagliate sui singoli in implementazioni di ciascuno di questi, ma molto poco su come mettere a finire insieme alla fine. Sembrerebbe che i libri che ho sono simile luce su questi temi anche con alcuna menzione di questo in "WCF Pro" da Peiris e Mulder.

Quello che sto puntando è qualcosa di simile alla seguente.

I messaggi vengono inviati e ricevuti deve essere formattato come qui di seguito in cui il nome del primo elemento è il nome dell'operazione che deve essere eseguito e l'elemento figlio è il payload del messaggio di richiesta sarebbe di forma:

<?xml version="1.0" encoding="UTF-8"?>
<op:DoSomething xmlns:op="http://my.ebXML.schema.com" xmlns:payload="http://payload.company.com">
    <op:AnObject>
        <payload:ImportantValue>42</payload:ImportantValue>
    </op:AnObject>
</op:DoSomething>

E la risposta potrebbe essere:

<?xml version="1.0" encoding="UTF-8"?>
<op:AcknowledgementResponse xmlns:op="http://my.ebXML.schema.com" xmlns:payload="http://payload.company.com">
    <op:ResponseObject>
        <payload:Ok>True</payload:Ok>
    </op:ResponseObject>
</op:AcknowledgementResponse>

Per quanto i messaggi sono tutti descritti da schemi XML ho usato XSD.exe per convertire questi per gli oggetti fortemente tipizzati. Vedere https://gist.github.com/740303 per gli schemi. Notare che questi sono esempi di schemi. Io non sono in grado di pubblicare i veri schemi senza violare gli accordi di riservatezza del cliente (e non vorrei si desidera anche me in quanto sono enormi).

Vorrei ora di essere in grado di scrivere l'implementazione del servizio nel seguente modo:

public class MyEndpoint : IMyEndpoint
{
    public AcknowledgementResponse DoSomething(AnObject value)
    {
        return new AcknowledgementResponse
            {
                Ok = True;
            };
    }
}

Qualsiasi aiuto sarebbe molto apprezzato.

È stato utile?

Soluzione

Non credo che avete bisogno di fare qualsiasi cosa con attacchi. Sto assumendo che è necessario inviare il messaggio ebXML formattato su HTTP comunque?

@ di Ladislav risposta è un approccio, ma penso che i codificatori di messaggi sono stati progettati per lavorare a un livello inferiore molto più di quello che si sta cercando di raggiungere. Sono essenzialmente i pezzi che codificano i messaggi da e verso il torrente sottostante (vale a dire, come il messaggio è rappresentato come byte sul torrente).

Penso che quello che dovete fare è implementare un personalizzato Messaggio Formatter . In particolare, dal momento che si dice che si desidera inviare i messaggi a una terza parte, quindi penso che sia solo l'interfaccia IClientMessageFormatter che è necessario implementare. L'altra interfaccia (IDispatchMessageFormatter) viene utilizzato sul lato server.

Sono necessari per attuare un ServiceBehavior appropriato e OperationBehavior per installare il formattatore nello stack, ma il codice per questo sarà minimo (la maggior parte del codice sarà nell'attuazione della sopra menzionata interfaccia).

Una volta implementato, è possibile utilizzare il "un metodo per elaborare tutti" approccio alla prova ed eseguire il debug del formattatore. Basta prendere il messaggio ricevuto e dump fuori alla console per la revisione, e quindi anche inviare una risposta indietro ebXML. Si potrebbe anche utilizzare lo stesso approccio per costruire il vostro unit test.

Altri suggerimenti

Dettagli della mia implementazione della risposta di Tim

Avevo bisogno di scrivere questo per il cliente Attualmente sto lavorando per così ho pensato che potrei pure postare qui anche. Spero che aiuti qualcuno. Ho creato un client di esempio e di servizio che ho usato per provare alcune di queste idee. Ho ripulito e ha aggiunto a Github. È possibile scaricarlo qui .

I seguenti cose necessarie da attuare per consentire WCF da utilizzare nel modo in cui avevo bisogno:

  1. WCF non aspettarsi un messaggio SOAP
  2. La possibilità di formattare i messaggi di richiesta e di risposta esattamente come richiesto
  3. Tutti i messaggi da considerare per la lavorazione
  4. I messaggi in arrivo per essere indirizzati al corretto funzionamento di gestirli

1. Configurare WCF di non aspettarsi un messaggio SOAP

Il primo passo è stato sempre il messaggio in arrivo attraverso il TextMessageEncoder. Ciò è stato ottenuto utilizzando un legame con l'impostazione MessageVersion.None sull'elemento textMessageEncoding personalizzato.

  <customBinding>
    <binding name="poxMessageBinding">
      <textMessageEncoding messageVersion="None" />
      <httpTransport />
    </binding>
  </customBinding>

2. Formattare il messaggio correttamente

Il messaggio formattatore è richiesto come il messaggio in arrivo è rifiutato di essere de-serializzato dal formattatore XML esistente senza l'aggiunta di attributi aggiuntivi sui contratti dei messaggi. Questo non sarebbe normalmente un problema, ma in esecuzione gli schemi dei miei clienti ebXML attraverso XSD.exe genera un file cs 33000 linea e non ho voglia di avere di modificare questo in alcun modo. Oltre la gente dimentica di aggiungere nuovamente gli attributi, in futuro, quindi questo rende la manutenzione più facile speriamo anche.

La formattazione personalizzata prevede di convertire il messaggio in arrivo al tipo del primo parametro e per convertire il tipo di ritorno in un messaggio di risposta. Esso controlla il metodo di implementazione per determinare i tipi del primo parametro e il valore di ritorno nel costruttore.

public SimpleXmlFormatter(OperationDescription operationDescription)
{
    // Get the request message type
    var parameters = operationDescription.SyncMethod.GetParameters();
    if (parameters.Length != 1)
        throw new InvalidDataContractException(
"The SimpleXmlFormatter will only work with a single parameter for an operation which is the type of the incoming message contract.");
    _requestMessageType = parameters[0].ParameterType;

    // Get the response message type
    _responseMessageType = operationDescription.SyncMethod.ReturnType;
}

E poi semplicemente utilizza il XmlSerializer per serializzare e deserializzare i dati. Una parte interessante di questo per me è stato l'uso di un BodyWriter personalizzato per serializzare l'oggetto nell'oggetto del messaggio. Qui di seguito è parte l'implementazione per il serializzatore servizio. L'implementazione del client è il contrario.

public void DeserializeRequest(Message message, object[] parameters)
{
    var serializer = new XmlSerializer(_requestMessageType);
    parameters[0] = serializer.Deserialize(message.GetReaderAtBodyContents());
}

public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
    return Message.CreateMessage(MessageVersion.None, _responseMessageType.Name,
                                 new SerializingBodyWriter(_responseMessageType, result));
}

private class SerializingBodyWriter : BodyWriter
{
    private readonly Type _typeToSerialize;
    private readonly object _objectToEncode;

    public SerializingBodyWriter(Type typeToSerialize, object objectToEncode) : base(false)
    {
        _typeToSerialize = typeToSerialize;
        _objectToEncode = objectToEncode;
    }

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        writer.WriteStartDocument();
        var serializer = new XmlSerializer(_typeToSerialize);
        serializer.Serialize(writer, _objectToEncode);
        writer.WriteEndDocument();
    }
}

3. Elaborano tutti i messaggi in arrivo

Al fine di istruire WCF per permettere a tutti i messaggi in arrivo da elaborare avevo bisogno di impostare la proprietà ContractFilter sulla EndpointDispatcher a un'istanza del MatchAllMessageFilter. Ecco un frammento che illustra questo da mia configurazione comportamento dell'endpoint.

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
    endpointDispatcher.ContractFilter = new MatchAllMessageFilter();
    // Do more config ...
}

4. Selezionando il corretto funzionamento

Ciò è stato possibile attraverso la creazione di una classe che implementa IDispatchOperationSelector. Nel metodo SelectOperation ho ispezionare il messaggio in arrivo. Qui sto cercando due cose:  1. un controllo di integrità che lo spazio dei nomi dell'elemento radice è la stessa come spazio dei nomi del contratto di servizio  2. Il nome dell'elemento radice (si noti l'uso di LocalName per rimuovere qualsiasi prefisso namespace)

Il nome dell'elemento radice è il valore di ritorno e che sarà associata a qualsiasi operazione sul contratto con un nome corrispondente o in cui l'attributo azione ha un valore corrispondente.

public string SelectOperation(ref Message message)
{
    var messageBuffer = message.CreateBufferedCopy(16384);

    // Determine the name of the root node of the message
    using (var copyMessage = messageBuffer.CreateMessage())
    using (var reader = copyMessage.GetReaderAtBodyContents())
    {
        // Move to the first element
        reader.MoveToContent();

        if (reader.NamespaceURI != _namespace)
            throw new InvalidOperationException(
"The namespace of the incoming message does not match the namespace of the endpoint contract.");

        // The root element name is the operation name
        var action = reader.LocalName;

        // Reset the message for subsequent processing
        message = messageBuffer.CreateMessage();

        // Return the name of the action to execute
        return action;
    }
}

Wrapping It All Up

Per facilitare l'implementazione ho creato un comportamento endpoint per gestire la configurazione del selettore messaggio formattatore, il filtro del contratto e il funzionamento. Potrei anche creato un legame per concludere la configurazione del binding personalizzato, ma non pensavo che una parte era troppo difficile da ricordare.

Una scoperta interessante per me era che il comportamento endpoint possibile impostare i messaggi di formattazione per tutte le operazioni del punto finale per usare un messaggio personalizzato formattatore. Ciò consente di risparmiare la necessità di configurare separatamente. Ho scelto questo piano da una delle campioni Microsoft .

Documentazione Link utili

Le migliori referenze che ho trovato finora sono la stazione di servizio MSDN articoli di riviste (Google "site: msdn.microsoft.com stazione di servizio WCF")

.

WCF Attacchi in Profondità - informazioni molto utili sulla configurazione attacchi

estensione WCF con comportamenti personalizzati - La migliore fonte di informazioni su punti dispatcher integrazione che ho trovato ancora e contengono alcuni diagrammi veramente utili che illustrano tutti i punti di integrazione e dove si verificano nell'ordine di lavorazione.

campioni Microsoft WCF - C'è molto qui dentro che non è documentato molto bene altrove. Ho trovato la lettura attraverso il codice sorgente di alcuni di questi molto istruttivo.

Per il formato messaggio personalizzato è necessario personalizzato MessageEncoder . MSDN contiene ad esempio come creare encoder personalizzato. Se si utilizza Reflector troverete diverse implementazioni di encoder in modo da poter imparare come scriverlo.

Si può anche verificare che cosa accadrà se si tenta di utilizzare TextMessageEncoder con MessageVersion.None (non ho mai tryed esso).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top