如何在控制台服务主机中包含 WCF 自定义标头

Posted

技术标签:

【中文标题】如何在控制台服务主机中包含 WCF 自定义标头【英文标题】:How to include WCF Custom Headers in console Service Host 【发布时间】:2014-06-22 23:53:52 【问题描述】:

在我的 WCF 服务中,我收到了 405 method not allowederror,然后遇到了一个帖子,建议在我的 WCF 主机的 Application_BeginRequest 中包含以下内容:

protected void Application_BeginRequest(object sender, EventArgs e)

    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
                    "Accept, Content-Type,customHeader");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods",
                    "POST,GET,OPTIONS");

        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age",
                    "172800");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials",
                    "true");

        HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers",
                    "customHeader");

        HttpContext.Current.Response.AddHeader("Content-type",
                    "application/json");

        HttpContext.Current.Response.End();
    
    else
    
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
                    "Accept, Content-Type,customHeader");

        HttpContext.Current.Response.AddHeader("Access-Control-Expose-Headers",
                    "customHeader");

        HttpContext.Current.Response.AddHeader("Content-type",
                    "application/json");
    
 

但我正在使用控制台应用程序托管我的服务。

using (ServiceHost sc = new ServiceHost(typeof(DataRetriever)))

    sc.Open();

    foreach (var endPoints in sc.Description.Endpoints)
    
        Console.WriteLine(endPoints.Address);
    

    Console.ReadKey();
    sc.Close();

那么如何在控制台应用程序中包含标题。

【问题讨论】:

我的情况一模一样,你找到解决办法了吗? 【参考方案1】:

在 WCF 中,可以通过 OperationContext 类的实例访问标头,该实例可通过 OperationContext.Current(如果可用)访问。

解决这个问题的简单方法是在你的服务方法中简单地使用这个属性:

[ServiceContract]
public interface IMyService

    [OperationContract]
    void MyMethod();


internal class MyService: IMyService

    public void MyMethod()
    
        Console.WriteLine("My Method");
        OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("headerFromMethod", "namespace", "headerFromMethodValue"));
    

为了完整起见,用于在控制台应用程序中托管此服务的代码(无需配置)是:

using (var serviceHost = new ServiceHost(typeof(MyService)))

    var endpoint = serviceHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "http://localhost:9000");

    serviceHost.Open();

    Console.WriteLine("Open for business");
    Console.ReadLine();

    serviceHost.Close();

.NET 客户端会像这样访问标头:

var channel = ChannelFactory<IMyService>.CreateChannel(new BasicHttpBinding(), new EndpointAddress("http://localhost:9000"));

var contextChannel = channel as IContextChannel;
using (new OperationContextScope(contextChannel))

    channel.MyMethod();

    var incommingHeaders = OperationContext.Current.IncomingMessageHeaders;
    var header = incommingHeaders.GetHeader<string>("headerFromMethod", "namespace");
    Console.WriteLine("Header from server: " + header);

如果您有Fiddler,您也可以使用此工具查看标题。

虽然此方法可以满足您的要求,但您是否要混合您的业务逻辑(包含在 IMyService 的实现中)和控制附加到消息。

通过实现IDispatchMessageInspector 可以获得更清晰的分离,它允许您拦截服务器上的调用并在消息传入和传出时修改消息:

public class ServerInterceptor: IDispatchMessageInspector, IEndpointBehavior

    object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    
        return null;
    

    void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
    
        reply.Headers.Add(MessageHeader.CreateHeader("header", "namespace", "headervalue"));
    

    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
    

    void IEndpointBehavior.Validate(ServiceEndpoint endpoint)

    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

从 .NET 客户端以与以前相同的方式访问标头。值得注意的是,您可以将信息从AfterReceiveRequest 方法传递给BeforeSendReply,因为前者方法中返回的对象在后者中作为correlationState 参数传递。如果您返回的标头取决于传入消息的标头,这将很有用 - 正如您的示例所暗示的那样。

最后,要在服务上安装这个功能,你需要修改托管代码如下:

...
var endpoint = serviceHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "http://localhost:9000");
endpoint.Behaviors.Add(new ServerInterceptor());
serviceHost.Open();
...

我们可以通过ServerInterceptor实现IEndpointBehavior这一事实来做到这一点

【讨论】:

我偶然发现了这篇文章,正在寻找一种更好的解决方案来将“Access-Control-Allow-Origin”添加到我的服务响应标头中。这让我很接近,但必须将 BeforeSendReply 中的代码更改为 HttpResponseMessageProperty prop = ( HttpResponseMessageProperty )reply.Properties[ "httpResponse" ]; prop.Headers.Add("Access-Control-Allow-Origin", "localhost"); BeforeSendReply 中的 Message 对象不包含 HTTP 标头信息,仅包含 SOAP 消息(至少对我而言)。如果我使用reply.Headers.Add() 方法,那么标头将添加到SOAP 消息中。使用 reply.Properties.Add() 时,什么也没有发生。也没有 reply.Properties["httpResponse"] 属性。我做错了什么?【参考方案2】:

这是可以做到的。您需要一个在主机(控制台)exe 和 Web 服务类之间运行的共享变量。在调用 WebService.Open() 后,您必须运行一个连续循环,检查此共享变量的输入。代码看起来像这样:

//this would be your console host class
public class HostInterface

    string sHeaderString;
    public static string HeaderString 
        get  return sHeaderString; 
        set  sHeaderString += value; 


   public void main()
   
      //code to start the web service
      System.ServiceModel.ServiceHost myWebService = default(System.ServiceModel.ServiceHost);

      //configure myWebService stuff
      myWebService.open();

      // here loop every second until the communication is stopped
      //check for new text in the shared sHeaderString 
      //written to by your web service class
      while (true) 
        if (myWebService.state != Communicationsstate.Opened)
            break; 
        
        //write message out through console
        console.writeline("Headers:" + sHeaderString);
        Threading.Thread.Sleep(1000);
        //sleep 1 second before going trying next
      
    
  

这将在您的 Web 服务的主类中,从您的控制台引用和更新“HeaderString”共享变量。

public void WriteHeadersToSharedVariable()

        //here, setting the headers into the shared variable instanced back on the console program
        HostInterface.Headerstring = GetHeadersFromRequest();


public string GetHeadersFromRequest()

    //some code to get the headers from inbound request
    return "blah blah blah";

希望你觉得这很有用。祝你好运。

【讨论】:

可能你误解了这个问题。

以上是关于如何在控制台服务主机中包含 WCF 自定义标头的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Jquery Ajax 发送自定义标头并使用 C# 在 WCF 服务中检索相同的标头值?

如何在 wcf 中添加自定义肥皂标题?

为啥 Access-Control-Request-Headers 中包含非自定义标头?

如何在 logback-spring.xml 中包含自定义 xml?

将 Miniprofiler 集成到控制台 WCF 自主机

WCF 作为 Windows 服务托管 - 自定义 HTTP 标头未反映在响应标头上