问题
In my WCF service I was getting 405 method not allowed
error and then came across a post which suggest to have the following in Application_BeginRequest
of my WCF host:
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");
}
}
But I am hosting my service using a console application.
using (ServiceHost sc = new ServiceHost(typeof(DataRetriever)))
{
sc.Open();
foreach (var endPoints in sc.Description.Endpoints)
{
Console.WriteLine(endPoints.Address);
}
Console.ReadKey();
sc.Close();
}
So how do I include the headers in the console app.
回答1:
In WCF, headers can be accessed via an instance of the class OperationContext, which is accessible via the OperationContext.Current (when available).
The naive way to approach this problem is to simply use this property within the method of your service:
[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"));
}
}
For completeness, the code used to host this service within the Console Application (no config required) is:
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();
}
A .NET client would access the headers like this:
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);
}
If you have Fiddler, you can also see the headers using this tool.
Whilst this method will do what you want, it is questionable whether you want to mix your business logic (contained within the implementation of IMyService
), and the logic controlling the "out-of-band" information attached to the message.
A cleaner separation is gained by implementing IDispatchMessageInspector, which allows you to intercept calls on the server and modify the message as it comes in and out:
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){}
}
The headers are accessed from a .NET client in the same way as before. It's worth noting that you can pass information from the AfterReceiveRequest
method to the BeforeSendReply
, as the object returned in the former method is passed as the correlationState
parameter in the latter. This would be useful if the headers you return are dependent on the headers of the incoming message - as your example suggests.
Finally, to install this functionality on the service, you need to modify the hosting code as follows:
...
var endpoint = serviceHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "http://localhost:9000");
endpoint.Behaviors.Add(new ServerInterceptor());
serviceHost.Open();
...
which we can do by virtue of the fact that ServerInterceptor
implements IEndpointBehavior
回答2:
This can be done. You'd need a shared variable operating between the host (console) exe and the web service classes. You'd have to run a continuous loop after calling WebService.Open(), checking this shared variable for input. The code would look something like this:
//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
}
}
}
}
this would be in the main class of your web service, referencing and updating the "HeaderString" shared variable from your console.
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";
}
Hopefully you find this usefull. Good luck.
来源:https://stackoverflow.com/questions/23493495/how-to-include-wcf-custom-headers-in-console-service-host