Ich habe einen WCF-Dienst, der in einem Windows-Dienst gehostet wird. Clients, die diesen Dienst verwenden, müssen jedes Mal, wenn sie Dienstmethoden aufrufen, einen Bezeichner übergeben (da dieser Bezeichner für die Funktionsweise der aufgerufenen Methode wichtig ist). Ich dachte, es ist eine gute Idee, diese Kennung irgendwie in die WCF-Header-Informationen einzufügen.
Wenn es eine gute Idee ist, wie kann ich den Bezeichner automatisch zu den Header-Informationen hinzufügen. Mit anderen Worten, wenn der Benutzer die WCF-Methode aufruft, muss der Bezeichner automatisch zum Header hinzugefügt werden.
UPDATE: Clients, die den WCF-Dienst verwenden, sind sowohl Windows-Anwendungen als auch Windows Mobile-Anwendungen (unter Verwendung von Compact Framework).
Antworten:
Dies hat den Vorteil, dass es auf jeden Anruf angewendet wird.
Erstellen Sie eine Klasse, die IClientMessageInspector implementiert . Fügen Sie in der BeforeSendRequest-Methode Ihren benutzerdefinierten Header zur ausgehenden Nachricht hinzu. Es könnte ungefähr so aussehen:
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { HttpRequestMessageProperty httpRequestMessage; object httpRequestMessageObject; if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) { httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty; if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER])) { httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent; } } else { httpRequestMessage = new HttpRequestMessageProperty(); httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent); request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage); } return null; }
Erstellen Sie dann ein Endpunktverhalten, das den Nachrichteninspektor auf die Client-Laufzeit anwendet. Sie können das Verhalten über ein Attribut oder über die Konfiguration mithilfe eines Verhaltenserweiterungselements anwenden.
Hier ist ein großartiges Beispiel dafür, wie Sie allen Anforderungsnachrichten einen HTTP-Benutzeragenten-Header hinzufügen. Ich benutze dies bei einigen meiner Kunden. Sie können dies auch auf der Serviceseite tun, indem Sie den IDispatchMessageInspector implementieren .
Hast du das gedacht?
Update: Ich habe diese Liste der WCF-Funktionen gefunden, die vom kompakten Framework unterstützt werden. Ich glaube Nachricht Inspektoren klassifizierten als ‚Kanal Extensibility‘, die nach diesem Beitrag werden durch den kompakten Rahmen unterstützt.
quelle
OutgoingMessageProperties
sind das, was Sie benötigen, um auf HTTP-Header zuzugreifen - nichtOutgoingMessageHeaders
SOAP-Header.Sie fügen es dem Anruf hinzu, indem Sie:
using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel)) { MessageHeader<string> header = new MessageHeader<string>("secret message"); var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com"); OperationContext.Current.OutgoingMessageHeaders.Add(untyped); // now make the WCF call within this using block }
Und dann greifen Sie serverseitig zu:
MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders; string identity = headers.GetHeader<string>("Identity", "http://www.my-website.com");
quelle
OperationContextScope
(undOperationContext
) sindThreadStatic
- Mark Goods Antwort funktioniert, ohne sich aufThreadStatic
Elemente zu verlassen.Wenn Sie nur allen Anforderungen an den Service denselben Header hinzufügen möchten, können Sie dies ohne jegliche Codierung tun!
Fügen Sie einfach den Header-Knoten mit den erforderlichen Headern unter dem Endpunktknoten in Ihrer Client-Konfigurationsdatei hinzu
<client> <endpoint address="http://localhost/..." > <headers> <HeaderName>Value</HeaderName> </headers> </endpoint>
quelle
MessageHeader
) - keine HTTP-Header.Hier ist eine weitere hilfreiche Lösung zum manuellen Hinzufügen benutzerdefinierter HTTP-Header zu Ihrer Client-WCF-Anforderung unter Verwendung von
ChannelFactory
als Proxy. Dies müsste für jede Anforderung durchgeführt werden, reicht jedoch als einfache Demo aus, wenn Sie Ihren Proxy nur zur Vorbereitung auf Nicht-.NET-Plattformen einem Komponententest unterziehen müssen.// create channel factory / proxy ... using (OperationContextScope scope = new OperationContextScope(proxy)) { OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty() { Headers = { { "MyCustomHeader", Environment.UserName }, { HttpRequestHeader.UserAgent, "My Custom Agent"} } }; // perform proxy operations... }
quelle
Dies ähnelt der Antwort von NimsDotNet, zeigt jedoch, wie dies programmgesteuert erfolgt.
Fügen Sie einfach den Header zur Bindung hinzu
var cl = new MyServiceClient(); var eab = new EndpointAddressBuilder(cl.Endpoint.Address); eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientIdentification", // Header Name string.Empty, // Namespace "JabberwockyClient")); // Header Value cl.Endpoint.Address = eab.ToEndpointAddress();
quelle
var endpoint = new EndpointAddress(new Uri(RemoteAddress), new[] { AddressHeader.CreateAddressHeader( "APIKey", "", "bda11d91-7ade-4da1-855d-24adfe39d174") });
quelle
Das funktioniert bei mir
TestService.ReconstitutionClient _serv = new TestService.TestClient(); using (OperationContextScope contextScope = new OperationContextScope(_serv.InnerChannel)) { HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers["apiKey"] = ConfigurationManager.AppSettings["apikey"]; OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage; _serv.Method(Testarg); }
quelle
Dies hat bei mir funktioniert, angepasst vom Hinzufügen von HTTP-Headern zu WCF-Aufrufen
// Message inspector used to add the User-Agent HTTP Header to the WCF calls for Server public class AddUserAgentClientMessageInspector : IClientMessageInspector { public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel) { HttpRequestMessageProperty property = new HttpRequestMessageProperty(); var userAgent = "MyUserAgent/1.0.0.0"; if (request.Properties.Count == 0 || request.Properties[HttpRequestMessageProperty.Name] == null) { var property = new HttpRequestMessageProperty(); property.Headers["User-Agent"] = userAgent; request.Properties.Add(HttpRequestMessageProperty.Name, property); } else { ((HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]).Headers["User-Agent"] = userAgent; } return null; } public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { } } // Endpoint behavior used to add the User-Agent HTTP Header to WCF calls for Server public class AddUserAgentEndpointBehavior : IEndpointBehavior { public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new AddUserAgentClientMessageInspector()); } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } }
Nachdem Sie diese Klassen deklariert haben, können Sie Ihrem WCF-Client das neue Verhalten folgendermaßen hinzufügen:
client.Endpoint.Behaviors.Add(new AddUserAgentEndpointBehavior());
quelle
Context - Bindungen in .NET 3.5 könnte nur sein , was Sie suchen. Es gibt drei sofort einsatzbereite: BasicHttpContextBinding, NetTcpContextBinding und WSHttpContextBinding. Das Kontextprotokoll übergibt grundsätzlich Schlüssel-Wert-Paare im Nachrichtenkopf. Schauen Sie sich Verwalten Staat mit dauerhaften Dienstleistungen Artikel auf MSDN Magazin.
quelle
Wenn ich Ihre Anforderung richtig verstehe, lautet die einfache Antwort: Sie können nicht.
Dies liegt daran, dass der Client des WCF-Dienstes möglicherweise von einem Dritten generiert wird, der Ihren Dienst verwendet.
Wenn Sie die Kontrolle über die Clients Ihres Dienstes haben, können Sie eine Basisclientklasse erstellen, die den gewünschten Header hinzufügt und das Verhalten der Worker-Klassen erbt.
quelle
Sie können benutzerdefinierte Header im MessageContract angeben .
Sie können auch <endpoint> -Header verwenden , die in der Konfigurationsdatei gespeichert sind und im Header aller vom Client / Service gesendeten Nachrichten kopiert werden. Dies ist nützlich, um einfach einen statischen Header hinzuzufügen.
quelle
MessageHeader
) - keine HTTP-Header.Wenn Sie jedem WCF-Aufruf objektorientiert benutzerdefinierte HTTP-Header hinzufügen möchten, suchen Sie nicht weiter.
Genau wie in der Antwort von Mark Good und Paulwhit müssen wir eine Unterklasse erstellen
IClientMessageInspector
, um die benutzerdefinierten HTTP-Header in die WCF-Anforderung einzufügen. Lassen Sie uns den Inspektor jedoch allgemeiner gestalten, indem Sie ein Wörterbuch akzeptieren, das die Header enthält, die wir hinzufügen möchten:public class HttpHeaderMessageInspector : IClientMessageInspector { private Dictionary<string, string> Headers; public HttpHeaderMessageInspector(Dictionary<string, string> headers) { Headers = headers; } public object BeforeSendRequest(ref Message request, IClientChannel channel) { // ensure the request header collection exists if (request.Properties.Count == 0 || request.Properties[HttpRequestMessageProperty.Name] == null) { request.Properties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty()); } // get the request header collection from the request var HeadersCollection = ((HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]).Headers; // add our headers foreach (var header in Headers) HeadersCollection[header.Key] = header.Value; return null; } // ... other unused interface methods removed for brevity ... }
Genau wie in der Antwort von Mark Good und Paulwhit müssen wir eine Unterklasse erstellen
IEndpointBehavior
, um unsereHttpHeaderMessageInspector
in unseren WCF-Client zu injizieren .public class AddHttpHeaderMessageEndpointBehavior : IEndpointBehavior { private IClientMessageInspector HttpHeaderMessageInspector; public AddHttpHeaderMessageEndpointBehavior(Dictionary<string, string> headers) { HttpHeaderMessageInspector = new HttpHeaderMessageInspector(headers); } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(HttpHeaderMessageInspector); } // ... other unused interface methods removed for brevity ... }
Der letzte Teil, der zum Abschluss unseres objektorientierten Ansatzes erforderlich ist, besteht darin, eine Unterklasse unseres automatisch generierten WCF-Clients zu erstellen (ich habe das WCF-Webdienst-Referenzhandbuch von Microsoft zum Generieren eines WCF-Clients verwendet).
In meinem Fall muss ich einen API-Schlüssel an den
x-api-key
HTML-Header anhängen .Die Unterklasse führt Folgendes aus:
EndpointConfiguration
Aufzählung generiert, die an den Konstruktor übergeben wird - möglicherweise hat Ihre Implementierung dies nicht).AddHttpHeaderMessageEndpointBehavior
an dasEndpoint
Verhalten des Kunden angehängtpublic class Client : MySoapClient { public Client(string apiKey) : base(EndpointConfiguration.SomeConfiguration) { var headers = new Dictionary<string, string> { ["x-api-key"] = apiKey }; var behaviour = new AddHttpHeaderMessageEndpointBehavior(headers); Endpoint.EndpointBehaviors.Add(behaviour); } }
Verwenden Sie zum Schluss Ihren Client!
var apiKey = 'XXXXXXXXXXXXXXXXXXXXXXXXX'; var client = new Client (apiKey); var result = client.SomeRequest()
Die resultierende HTTP-Anforderung sollte Ihre HTTP-Header enthalten und ungefähr so aussehen:
POST http://localhost:8888/api/soap HTTP/1.1 Cache-Control: no-cache, max-age=0 Connection: Keep-Alive Content-Type: text/xml; charset=utf-8 Accept-Encoding: gzip, deflate x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXX SOAPAction: "http://localhost:8888/api/ISoapService/SomeRequest" Content-Length: 144 Host: localhost:8888 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <SomeRequestxmlns="http://localhost:8888/api/"/> </s:Body> </s:Envelope>
quelle
Ein bisschen spät zur Party, aber Juval Lowy spricht genau dieses Szenario in seinem Buch und der zugehörigen ServiceModelEx- Bibliothek an.
Grundsätzlich definiert er ClientBase- und ChannelFactory-Spezialisierungen, mit denen typsichere Headerwerte angegeben werden können. Ich empfehle, die Quelle herunterzuladen und die Klassen HeaderClientBase und HeaderChannelFactory zu betrachten.
John
quelle