Her WCF çağrısına özel bir HTTP başlığı nasıl eklenir?


162

Bir Windows Hizmeti'nde barındırılan bir WCF hizmetim var. Bu hizmeti kullanan istemciler, hizmet yöntemlerini her aradıklarında bir tanımlayıcı iletmelidir (çünkü bu tanımlayıcı, çağrılan yöntemin yapması gereken şey için önemlidir). Bu tanımlayıcıyı WCF başlık bilgilerine bir şekilde koymanın iyi bir fikir olduğunu düşündüm.

İyi bir fikirse, tanımlayıcıyı başlık bilgilerine otomatik olarak nasıl ekleyebilirim? Başka bir deyişle, kullanıcı WCF yöntemini her çağırdığında, tanımlayıcının otomatik olarak başlığa eklenmesi gerekir.

GÜNCELLEME: WCF hizmetini kullanan istemciler hem Windows uygulamaları hem de Windows Mobile uygulamasıdır (Compact Framework kullanarak).


1
Sorununuzu çözebildiniz mi?
Mark Good

Sonunda bunu Kompakt Çerçeve üzerinde çalışmaya başladınız mı?
Vaccano

Yanıtlar:


185

Bunun avantajı, her çağrıya uygulanmasıdır.

IClientMessageInspector uygulayan bir sınıf oluşturun . BeforeSendRequest yönteminde, özel başlığınızı giden iletiye ekleyin. Bunun gibi bir şey olabilir:

    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;
}

Ardından, ileti denetçisini istemci çalışma zamanına uygulayan bir uç nokta davranışı oluşturun. Davranışı bir öznitelik yoluyla veya bir davranış uzantısı öğesi kullanarak yapılandırma yoluyla uygulayabilirsiniz.

Aşağıda, tüm istek iletilerine HTTP kullanıcı aracısı üstbilgisinin nasıl ekleneceğine dair harika bir örnek verilmiştir. Bunu birkaç müşterimde kullanıyorum. Aynı işlemi servis tarafında da IDispatchMessageInspector uygulamasını uygulayarak yapabilirsiniz .

Aklında olan bu muydu?

Güncelleme: Kompakt çerçeve tarafından desteklenen bu WCF özellikleri listesini buldum . Ben bu yazı uyarınca, 'Kanal Extensibility' olarak sınıflandırılması mesaj müfettişleri inanıyoruz olan kompakt çerçeve ile desteklenen.


2
@ Mark, bu gerçekten harika bir cevap. Teşekkürler. Bunu net.tcp üzerinden denedim ama doğrudan Headers koleksiyonunu kullanıyorum (Http Headers işe yaramadı). ServiceHost AfterReceiveRequest olayında benim belirteci (Name) ile bir Başlık olsun, ama değer (hatta bir değer için bir özellik gibi görünmüyor?). Kaçırdığım bir şey var mı? İstediğim başlığı oluştururken bir ad / değer çifti beklerdim: request.Headers.Add (MessageHeader.CreateHeader (name, ns, value));
Program.X

13
+1 OutgoingMessageProperties, HTTP Üstbilgilerine erişmek için gerekenlerdir - OutgoingMessageHeadersSOAP üstbilgileri değildir.
SliverNinja - MSFT

1
Basitçe, Müthiş Kod! :)
abhilashca

3
Bu yalnızca - verilen örneğe göre - web.config dosyasında sabit kodlanmış bir sabit kodlu kullanıcı aracısına izin verir.
KristianB

1
Bu mükemmel bir cevap. Ayrıca, HttpRequestMessageProperty.Name ileti özelliklerinde henüz kullanılabilir olmadığında da ele alır. Nedense, benim kod hata ayıklama, bazı zamanlama sorunlarına bağlı olarak bu değerin her zaman orada olmadığını fark ettim. Teşekkürler Mark!
carlos357

80

Aşağıdakileri kullanarak aramaya eklersiniz:

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
}

Ve sonra, sunucu tarafında kullanarak kapmak:

MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
string identity = headers.GetHeader<string>("Identity", "http://www.my-website.com");

5
Kod pasajınız için teşekkürler. Ancak bununla her yöntemi çağırmak istediğimde başlığı eklemeliyim. Bu süreci şeffaf hale getirmek istedim. Yani bir kez uygulamakla, kullanıcı bir hizmet istemcisi her oluşturduğunda ve bir yöntem kullandığında, müşteri başlığı iletiye otomatik olarak eklenir.
mrtaikandi

Bu, bu yanıtta verilen öneriyi genişletmek için iyi bir MSDN bağlantısıdır: msdn.microsoft.com/en-us/library/…
atconway

1
Teşekkürler, özel bir istemci kitaplığı kullanıyorsanız, bu büyük bir kod parçasıdır. Bu şekilde mesaj denetçisini uygulamanız gerekmez. OperationContextScope içindeki her istemci çağrısını saran ortak bir sarma yöntemi oluşturun.
JustAMartin

3
Not olarak, aramalarınızla herhangi bir zaman uyumsuz şeyler yapıyorsanız sorunludur, çünkü OperationContextScope(ve OperationContext) ThreadStatic- Mark Good'un cevabı, ThreadStaticöğelere güvenmeden çalışacaktır .
zimdanen

2
Bu bir HTTP başlığı eklemez! SOAP zarfına üstbilgiler ekler.
br3nt

32

Hizmete yapılan tüm isteklere aynı üstbilgiyi eklemek istiyorsanız, bunu herhangi bir kodlama olmadan yapabilirsiniz!
İstemci yapılandırma dosyanızdaki uç nokta düğümünün altına gerekli başlıklarla başlık düğümünü eklemeniz yeterlidir

<client>  
  <endpoint address="http://localhost/..." >  
    <headers>  
      <HeaderName>Value</HeaderName>  
    </headers>   
 </endpoint>  

18
Bunlar SOAP Üstbilgileri ( alaMessageHeader ) - HTTP Üstbilgileri değil.
SliverNinja - MSFT

18

ChannelFactoryProxy olarak kullanarak istemci WCF isteğinize manuel olarak özel HTTP Üstbilgileri eklemek için başka bir yararlı çözüm . Bu, her istek için yapılmalıdır, ancak proxy olmayan platformlara hazırlanmak için proxy'nizi birim olarak test etmeniz gerekiyorsa, basit bir demo olarak yeterlidir.

// 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... 
}

1
Diğer benzer görünümlü 4 öneri denedim ve bu benim için çalıştı tek.
JohnOpincar

Bu aslında HTTP üstbilgileri ekler, teşekkürler! :) Ama jeez çirkin görünümlü kod.
br3nt

11

Bu NimsDotNet yanıtına benzer, ancak programlı olarak nasıl yapılacağını gösterir.

Üstbilgiyi ciltlemeye eklemeniz yeterlidir

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();

Bu kodu geçerli çağrı (istemci tarafı) eklendi var .. System.ServiceModel.OperationContext bu kafa değerini nasıl alabilirim? (sunucu tarafı) (Bu bana yardımcı olacak parmaklarımı
geçiyorum

1
Anladım ! System.ServiceModel.Channels.MessageHeaders üstbilgileri = operationContext.RequestContext.RequestMessage.Headers; int headerIndex = headers.FindHeader ("ClientIdentification", string.Empty); var requestName = (headerIndex <0)? "BİLİNMİYOR": headers.GetHeader <string> (headerIndex);
granadaCoder

1
@granadaCoder Bu siteyi seviyorum! ;-)
ΩmegaMan

Bu, HTTP başlığı değil SOAP zarfına bir başlık ekler
br3nt

5
var endpoint = new EndpointAddress(new Uri(RemoteAddress),
               new[] { AddressHeader.CreateAddressHeader(
                       "APIKey", 
                       "",
                       "bda11d91-7ade-4da1-855d-24adfe39d174") 
                     });

12
Bu bir SOAP ileti üstbilgisi, HTTP üstbilgisi değil.
René

3

WCF Çağrılarına HTTP Üstbilgileri Ekleme'den uyarlanmış olan bu benim için işe yaradı

// 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)
    {
    }
}

Bu sınıfları bildirdikten sonra yeni davranışı WCF istemcinize şu şekilde ekleyebilirsiniz:

client.Endpoint.Behaviors.Add(new AddUserAgentEndpointBehavior());

Bu derlenmez: Hata CS0136 Bu ad, bir yerel veya parametre tanımlamak için çevreleyen bir yerel kapsamda kullanıldığından, 'özellik' adlı bir yerel veya parametre bu kapsamda bildirilemiyor.
Leszek P

sadece kullanılmayan birini kaldırın
kosnkov

3

Bu benim için çalışıyor

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);
}

2

.NET 3.5'teki bağlam bağlamaları tam aradığınız şey olabilir. Kutunun dışında üç tane vardır: BasicHttpContextBinding, NetTcpContextBinding ve WSHttpContextBinding. Bağlam protokolü temel olarak anahtar / değer çiftlerini ileti başlığına iletir. MSDN dergisindeki Dayanıklı Hizmetlerle Durumu Yönetme makalesine göz atın .


Ayrıca, sunucuyla oturum açmadan önce içeriği yalnızca bir kez ayarladığınızı unutmayın. Sonra bağlam salt okunur hale gelir. Bağlam kurulumunun istemci tarafında saydam olmasını istiyorsanız, istemci proxt sınıfından türetilebilir ve yapıcıya bağlamınızı oluşturan bilgileri ekleyebilirsiniz. Daha sonra istemci her istemci proxt örneği oluşturduğunda, içerik otomatik olarak oluşturulur ve istemci proxy örneğine eklenir.
Mehmet Aras

2

İhtiyacınızı doğru anlarsam, basit cevap: yapamazsınız.

Bunun nedeni, WCF hizmetinin istemcisinin hizmetinizi kullanan herhangi bir üçüncü tarafça oluşturulmuş olabilmesidir.

EĞER hizmetinizin müşterilerin kontrole sahip, istenen başlık eklemek ve işçi sınıfları üzerine davranışı miras bir temel istemci sınıfını oluşturabilirsiniz.


1
Gerçekten SOA oluşturuyorsanız, tüm istemcilerin .NET tabanlı olduğunu varsayamazsınız. İşletmeniz edinilene kadar bekleyin.
SliverNinja - MSFT

2
Bu gerçekten doğru mu? Java web hizmeti istemcileri SOAP başlıklarına ad / değer ekleme yeteneğine sahip değil misiniz? Buna inanmakta zorlanıyorum. Elbette farklı bir uygulama olurdu, ancak bu birlikte çalışabilir bir çözümdür
Adam


0

Her WCF çağrısına nesne yönelimli şekilde özel HTTP üstbilgileri eklemek istiyorsanız, daha fazla kendinizi yormayın.

Mark Good ve paulwhit'in cevabında olduğu gibi, IClientMessageInspectorözel HTTP başlıklarını WCF isteğine enjekte etmek için alt sınıfa ihtiyacımız var . Ancak, eklemek istediğimiz başlıkları içeren bir sözlüğü kabul ederek denetçiyi daha genel hale getirelim:

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 ...
}

Mark Good'un ve paulwhit'in cevabında olduğu gibi, WCF müşterimize IEndpointBehaviorenjekte etmek için alt sınıfa ihtiyacımız var HttpHeaderMessageInspector.

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 ...
}

Nesneye yönelik yaklaşımımızı tamamlamak için gereken son bölüm, WCF otomatik olarak oluşturulan istemcimizin bir alt sınıfını oluşturmaktır (Microsoft'un WCF Web Hizmeti Başvuru Kılavuzu'nu kullandım) WCF istemcisi oluşturmak için kullandım).

Benim durumumda, bir API anahtarı x-api-key HTML başlığına .

Alt sınıf aşağıdakileri yapar:

  • temel sınıfın yapıcısını gerekli parametrelerle çağırır (benim durumumda bir EndpointConfiguration yapıcıya geçmek için numaralandırma oluşturuldu - belki uygulamanız buna sahip olmayacaktır)
  • Her talebe eklenmesi gereken başlıkları tanımlar
  • AddHttpHeaderMessageEndpointBehaviorİstemcinin Endpointdavranışlarına iliştirilir
public 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);
    }
}

Son olarak, müşterinizi kullanın!

var apiKey = 'XXXXXXXXXXXXXXXXXXXXXXXXX';
var client = new Client (apiKey);
var result = client.SomeRequest()

Ortaya çıkan HTTP isteği HTTP başlıklarınızı içermeli ve şöyle görünmelidir:

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>

-1

Partiye biraz geç ama Juval Lowy kitabında ve ilgili ServiceModelEx kütüphanesinde bu senaryoyu ele alıyor .

Temelde tip-güvenli başlık değerlerinin belirlenmesine izin veren ClientBase ve ChannelFactory uzmanlıklarını tanımlar. Kaynak indirmeyi ve HeaderClientBase ve HeaderChannelFactory sınıflarına bakmayı öneriyorum.

John


1
Bu, birisinin çalışmasını teşvik etmekten başka bir şey değildir. Alakalı bir alıntı / algoritma - yani soruyu cevaplamak - veya herhangi bir bağlantınızı açıklamak ister misiniz? Aksi takdirde bu sadece istenmeyen posta.
Monica'nın Davası

Birinin farkında olmayabilecekleri bir yaklaşıma işaretçi olarak cevap verdiğini söyleyebilirim. İlgili bağlantıyı verdim, neden daha fazlasını eklemeliyim? hepsi referanslarda. Ve eminim Juval Lowy bunu şimdiye kadar yapabileceğimden daha iyi tarif edebilirdi :-) Bağlantıya gelince - kitabı satın aldım! Bu kadar. Bay Lowy ile hiç tanýţmadým ama eminim harika bir adam. Görünüşe göre WCF hakkında çok şey biliyor ;-)
BrizzleOwl

Daha fazla eklemelisiniz, çünkü muhtemelen cevaplamadan önce Nasıl Yanıt Verilir'i okudunuz ve "Hedef siteye erişilemiyorsa veya kalıcı olarak çevrimdışı olursa, her zaman önemli bir bağlantının en alakalı kısmını belirtin" bölümünü not ettiniz. Üyeliğiniz önemli değil. Sadece cevap kalitesi.
Monica'nın Davası

İnce. Puanlar için değil - muhtemelen benim puanından söyleyebilirim! Sadece yararlı bir işaretçi olabileceğini düşündüm.
BrizzleOwl

1
Kötü bir işaretçi olduğunu söylemiyorum. Kendi başına iyi bir cevap olmadığını söylüyorum. İnsanlara çok yardımcı olabilir ve bu iyi bir şeydir, ancak ilgili sınıfları kısaca tanımlamak yerine, kullandığı yöntemi tanımlayabilirseniz cevap daha iyi olacaktır. Bu şekilde, siteye erişilememesi durumunda - herhangi bir nedenle - cevabınız hala yardımcı olur.
Monica'nın Davası
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.