webentwicklung-frage-antwort-db.com.de

Verwendung von IDispatchMessageInspector in einem WCF-Dienst

Ich versuche, IDispatchMessageInspector in einer WCF-Dienstimplementierung zu verwenden, um auf benutzerdefinierte Headerwerte zuzugreifen.

So etwas wie:

public class MyService : IMyService
{
    public List<string> GetNames()
    {
        var headerInspector = new CustomHeaderInspector();

        // Where do request & client channel come from?
        var values = headerInspector.AfterReceiveRequest(ref request, clientChannel, OperationContext.Current.InstanceContext);            
    }
}

Ich habe meine eigene IDispatchMessageInspector-Klasse implementiert.

public class CustomHeaderInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
        var userName = prop.Headers["Username"];

        return userName;
    }
}

Wie komme ich vorbei?

  • System.ServiceModel.Channels. Message und

  • System.ServiceModel. IClientChannel

to AfterReceiveRequest called von der Service-Implementierung?

BEARBEITEN:

Viele Artikel wie dieser oder dieser geben Beispiele für die Implementierung Ihres eigenen ServiceBehaviorname__. So sieht Ihre Service-Implementierung aus:

[MyCustomBehavior]
public class MyService : IMyService
{
    public List<string> GetNames()
    {
        // Can you use 'MyCustomBehavior' here to access the header properties?
    }
}

Kann ich also auf MyCustomBehaviorirgendwie innerhalb der Service-Operationsmethode zugreifen, um auf benutzerdefinierte Header-Werte zuzugreifen?

12
Didaxis

Sie müssen das konfigurieren

<extensions>
  <behaviorExtensions>
    <add 
      name="serviceInterceptors" 
      type="CustomHeaderInspector , MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
    />
  </behaviorExtensions>
</extensions>

Die Erweiterung wird dann in Ihrem WCF-Stapel behandelt. Der Dienst selbst kennt die serviceInterceptors nicht und Sie müssen nicht so etwas tun wie in Ihrem ersten Codeblock. Der WCF-Stapel wird Ihnen Inspector injizieren.

MSDN: system.servicemodel.dispatcher.idispatchmessageinspector

6
Peter

Ich benutze IClientMessageInspector für das gleiche Ziel. So können Sie sie aus dem Code anwenden:

 var serviceClient = new ServiceClientClass(binding, endpointAddress);
serviceClient.Endpoint.Behaviors.Add(
            new MessageInspectorEndpointBehavior<YourMessageInspectorType>());


/// <summary>
/// Represents a run-time behavior extension for a client endpoint.
/// </summary>
public class MessageInspectorEndpointBehavior<T> : IEndpointBehavior
    where T: IClientMessageInspector, new()
{
    /// <summary>
    /// Implements a modification or extension of the client across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that is to be customized.</param>
    /// <param name="clientRuntime">The client runtime to be customized.</param>
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new T());
    }

    /// <summary>
    /// Implement to pass data at runtime to bindings to support custom behavior.
    /// </summary>
    /// <param name="endpoint">The endpoint to modify.</param>
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implements a modification or extension of the service across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that exposes the contract.</param>
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implement to confirm that the endpoint meets some intended criteria.
    /// </summary>
    /// <param name="endpoint">The endpoint to validate.</param>
    public void Validate(ServiceEndpoint endpoint)
    {
        // Nothing special here
    }
}

Und hier ist eine Beispielimplementierung von MessageInspector, mit der ich die Clientversion an den Server übergebe und die Serverversion in benutzerdefinierten Headern abrufe:

/// <summary>
/// Represents a message inspector object that can be added to the <c>MessageInspectors</c> collection to view or modify messages.
/// </summary>
public class VersionCheckMessageInspector : IClientMessageInspector
{
    /// <summary>
    /// Enables inspection or modification of a message before a request message is sent to a service.
    /// </summary>
    /// <param name="request">The message to be sent to the service.</param>
    /// <param name="channel">The WCF client object channel.</param>
    /// <returns>
    /// The object that is returned as the <paramref name="correlationState " /> argument of
    /// the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply([email protected],System.Object)" /> method.
    /// This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid" /> to ensure that no two
    /// <paramref name="correlationState" /> objects are the same.
    /// </returns>
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        request.Headers.Add(new VersionMessageHeader());
        return null;
    }

    /// <summary>
    /// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
    /// </summary>
    /// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
    /// <param name="correlationState">Correlation state data.</param>
    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        var serverVersion = string.Empty;
        var idx = reply.Headers.FindHeader(VersionMessageHeader.HeaderName, VersionMessageHeader.HeaderNamespace);
        if (idx >= 0)
        {
            var versionReader = reply.Headers.GetReaderAtHeader(idx);
            while (versionReader.Name != "ServerVersion"
                   && versionReader.Read())
            {
                serverVersion = versionReader.ReadInnerXml();
                break;
            }
        }

        ValidateServerVersion(serverVersion);
    }

    private static void ValidateServerVersion(string serverVersion)
    {
        // TODO...
    }
}

public class VersionMessageHeader : MessageHeader
{
    public const string HeaderName = "VersionSoapHeader";
    public const string HeaderNamespace = "<your namespace>";
    private const string VersionElementName = "ClientVersion";

    public override string Name
    {
        get { return HeaderName; }
    }

    public override string Namespace
    {
        get { return HeaderNamespace; }
    }

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteElementString(
            VersionElementName,
            Assembly.GetExecutingAssembly().GetName().Version.ToString());
    }
}
5
Woodman

Was ich getan habe, um auf die Details zuzugreifen, habe ich in IDispatchMessageInspector.AfterReceiveRequest eingestellt

Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username, "Membership Provider"), roles);

Ich habe den Authentifizierungscode weggelassen.

Um über die Dienstmethode auf den Wert zuzugreifen, können Sie anrufen

Thread.CurrentPrincipal.Identity.Name

1
3dd

Ich glaube, Sie müssen custom IDispatchMessageInspector nicht implementieren, um benutzerdefinierte Header abzurufen.

var mp = OperationContext.Current.IncomingMessageProperties;
var property = (HttpRequestMessageProperty)mp[HttpRequestMessageProperty.Name];
var userName = property.Headers["Username"];

Es ist sinnvoll, einen benutzerdefinierten Inspektor für Versandnachrichten zu implementieren, wenn Sie die Nachrichtenverarbeitung abbrechen möchten, z. B. wenn Anmeldeinformationen fehlen. In diesem Fall können Sie einfach FaultException auslösen.

Wenn Sie dennoch einen Wert vom Dispatch-Message-Inspector an die Service-Operationsmethode übergeben möchten, kann dieser wahrscheinlich zusammen mit der Aufruf-ID (Sitzungs-ID) über einen Singleton übergeben werden, um ihn später von der Methode zu extrahieren, oder mit den Erweiterungen wcf

1
Alexander

Auf der von Ihnen verknüpften MSDN-Seite finden Sie auch eine Beschreibung, wie ein Inspektor eingefügt werden kann, und ein Beispiel dafür. Zitieren:

Normalerweise werden Nachrichteninspektoren durch ein Dienstverhalten, ein Endpunktverhalten oder ein Vertragsverhalten eingefügt. Das Verhalten fügt dann den Nachrichteninspektor zur Auflistung DispatchRuntime.MessageInspectors hinzu.

Später haben Sie folgende Beispiele:

  • Implementieren von benutzerdefiniertem IDispatchMessageInspector
  • Implementieren von benutzerdefiniertem IServiceBehavior, das den Inspektor zur Laufzeit hinzufügt.
  • Konfiguration des Verhaltens über die .config-Datei.

Das sollte ausreichen, um Ihnen den Einstieg zu erleichtern. Ansonsten kannst du gerne fragen :)

Wenn Sie nur Zugriff auf Header aus Ihrem Dienst erhalten möchten, können Sie OperationContext.Current.IncomingMessageHeaders versuchen.

0
Patko