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

问题描述 投票:5回答:2

在我的WCF服务中,我遇到405 method not allowed错误,然后遇到一则帖子,提示在我的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();
}

所以我如何在控制台应用程序中包含标题。

c# wcf cross-domain same-origin-policy
2个回答
10
投票

在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获得了更清晰的分隔,它使您可以拦截服务器上的呼叫并在消息进出时对其进行修改:

IDispatchMessageInspector

与以前一样,可以从.NET客户端访问标头。值得注意的是,您可以将信息从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){} } 方法传递到AfterReceiveRequest,因为前一种方法中返回的对象将作为后一种中的BeforeSendReply参数传递。如果您返回的标题取决于传入消息的标题,这将很有用-如您的示例所示。

最后,要在服务上安装此功能,您需要按如下所示修改托管代码:

correlationState

我们可以借助... var endpoint = serviceHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "http://localhost:9000"); endpoint.Behaviors.Add(new ServerInterceptor()); serviceHost.Open(); ... 实现ServerInterceptor的事实来做>

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

IEndpointBehavior

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

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

希望您发现这很有用。祝好运。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.