Following is the wsdl file of my service:
<wsdl:types>
<xsd:schema targetNamespace="http://tempuri.org/Imports">
<xsd:import schemaLocation="http://localhost:3789/VideoUpload.svc?xsd=xsd0" namespace="http://tempuri.org/" />
<xsd:import schemaLocation="http://localhost:3789/VideoUpload.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/" />
<xsd:import schemaLocation="http://localhost:3789/VideoUpload.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/UploadVideoProtocol" />
</xsd:schema>
</wsdl:types>
-----
<wsdl:definitions>
<wsdl:service name="VideoUpload">
<wsdl:port name="BasicHttpBinding_IVideoUpload" binding="tns:BasicHttpBinding_IVideoUpload">
<soap:address location="http://localhost:3789/VideoUpload.svc" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
In the above, I could change the namespace by specifying the custom namespace inside the code in service contract and behavior.
But I need to change the endpoint address specified in the schema location,
schemaLocation="http://localhost:3789/VideoUpload.svc?xsd=xsd0"
To my own defined endpoint address as:
schemaLocation="http://myservice.com:8080/VideoUpload.svc?xsd=xsd0"
What is the procedure to achieve this? what has to be mentioned in the code to change the default endpoint generated? Can anyone please help me on this?
You could dynamically update the WCF endpoint address in the WSDL meta data by adding a new behavior that implements "IWsdlExportExtension"
public class HostNameAddressBehavior : Attribute, IWsdlExportExtension, IEndpointBehavior, IServiceBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void ExportContract(WsdlExporter exporter,
WsdlContractConversionContext context)
{
}
/// <summary>
/// Overwrite service meta data
/// </summary>
/// <param name="exporter"></param>
/// <param name="context"></param>
public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
{
var address = "YOUR_ENDPOINT";
context.Endpoint.Address = new System.ServiceModel.EndpointAddress(address);
XmlSchemaSet schemaSet = exporter.GeneratedXmlSchemas;
foreach (System.Web.Services.Description.ServiceDescription wsdl in exporter.GeneratedWsdlDocuments)
{
foreach (XmlSchema schema in wsdl.Types.Schemas)
{
ChangeSchemaLocation(schemaSet, schema, address);
}
}
}
/// <summary>
/// Update XSD location
/// </summary>
/// <param name="xmlSchemaSet"></param>
/// <param name="xsdDoc"></param>
/// <param name="address"></param>
private void ChangeSchemaLocation(XmlSchemaSet xmlSchemaSet, XmlSchema xsdDoc, string address)
{
foreach (XmlSchemaExternal external in xsdDoc.Includes)
{
if ((external != null) && string.IsNullOrEmpty(external.SchemaLocation))
{
string str = (external is XmlSchemaImport) ? ((XmlSchemaImport)external).Namespace : xsdDoc.TargetNamespace;
foreach (XmlSchema schema in xmlSchemaSet.Schemas(str ?? string.Empty))
{
if (schema != xsdDoc)
{
external.SchemaLocation = address + "/?xsd=xsd0"; // set the location;
break;
}
}
continue;
}
}
}
}
Add your new behavior by code or in the config file.
By code:
var endpoint = listener.ServiceHost.Description.Endpoints.First();
endpoint.Behaviors.Add(new HostNameAddressBehavior());
OR
By Config:
Create extension:
public class HostNameAddressBehaviorExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get
{
return typeof(HostNameAddressBehavior);
}
}
protected override object CreateBehavior()
{
return new HostNameAddressBehavior();
}
}
Then add:
<extensions>
<behaviorExtensions>
<add name="hostNameAddress" type="YourService.HostNameAddressBehaviorExtension, YourService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
Related
I am looking for a way to execute specific method, at the server side, on every request method's call.
This is for security validations but not only.
This is NOT duplicated with this question since we mean to completely different things and. I addition, all the relevant answers there have unavailable links so it's impossible to get to the right answer.
(Sorry I haven't attached any code here, there is no code to specify in this issue).
The best solution is to create WCF custom behavior.
Here is how you do this by several simple steps:
Client Side:
public class FillHeaderDataBehaviourExtension : BehaviorExtensionElement, IEndpointBehavior
{
#region BehaviorExtensionElement Implementation
public override Type BehaviorType
{
get
{
return typeof(FillHeaderDataBehaviourExtension);
}
}
protected override object CreateBehavior()
{
return this;
}
#endregion
#region IServiceBehaviour Implementation
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new MessageInspector());
}
#endregion
}
public class MessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("HeaderData", String.Empty, HeaderDataVM.GetInstance().GetBaseInstance());
request.Headers.Add(header); // There is no need for checking if exist before adding. Every request has it's own headers.
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
Server Side:
public class ExtractHeadersBehaviourExtension : BehaviorExtensionElement, IServiceBehavior
{
#region BehaviorExtensionElement Implementation
public override Type BehaviorType
{
get
{
return typeof(ExtractHeadersBehaviourExtension);
}
}
protected override object CreateBehavior()
{
return this;
}
#endregion
#region IServiceBehavior Implementation
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
for (int i = 0; i < serviceHostBase.ChannelDispatchers.Count; i++)
{
ChannelDispatcher channelDispatcher = serviceHostBase.ChannelDispatchers[i] as ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
MessageInspector inspector = new MessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
#endregion
}
public class MessageInspector : IDispatchMessageInspector
{
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HeaderData headerData = request.Headers.GetHeader<HeaderData>("HeaderData", String.Empty);
if(headerData != null)
{
OperationContext.Current.IncomingMessageProperties.Add("HeaderData", headerData);
}
return null;
}
}
And finally, don't forget to configure it in the app.config files (client & server side) as follows:
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior">
<fillHeaderDataBehaviourExtension/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
You can also add these lines via the WCF config editor. To do so, look at this answer.
EDIT: You might get an error in the app config after adding these lines of configuration code:
Don't worry about this, your application will run fine. It causes because the GAC (Global Assembly Cache) folder doesn't contain this behavior (since it is a custom behavior). You can fix it by adding this behavior manually to your GAC folder on your computer.
However, this error might prevent you from updating service reference. If you try to, you'll get this error message:
So just comment out this line (<extractHeadersBehaviourExtension/>) (in client & server side) when you update your service reference.
Sources: How to add behavior on a specific endpoint? &
Adding Custom Message Headers to a WCF Service
I've currently rigged up Ninject into a WCF application (using ninject.extensions.wcf), and the basics are working fine. However, I've added a custom IDispatchMessageInspector and IServiceBehavior in order to authenticate each service call (by check the database against some credentials), but I'm not entirely sure how to get Ninject working within the IDispatchMessageInspector.
The code I have so far is:
public class MyServiceInspector: IDispatchMessageInspector
{
#region Methods
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// TODO: Get credentials from request here, and hit database.
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
// Do nothing
}
#endregion
}
The IServiceBehavior which initiates this is as follows:
public class MyServiceBehaviour : Attribute, IServiceBehavior
{
#region Methods
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
// Do nothing
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
// Loop through channels and endpoints
foreach (ChannelDispatcher cDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
{
// Add credentials inspector
eDispatcher.DispatchRuntime.MessageInspectors.Add(new MyServiceInspector());
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
// Do nothing
}
#endregion
}
I've scoured the internet looking for examples, but I can't seem to find anything. Is this even possible?
I know this answer is very late, but hopefully it helps other people coming across this issue.
I had the same problem today.. I needed a message inspector with injected dependencies. They added a NinjectBehaviorExtensionElement class to Ninjext.Extensions.WCF from version 3.2. So here's how i injected dependencies in my Message Inspector:
The Message Inspector:
public class MyMessageInspector : IDispatchMessageInspector
{
public MyMessageInspector(IInjectedDependency injectedDependency)
{
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
}
The Behavior:
public class MyMessageInspectionBehavior : IEndpointBehavior
{
private readonly IDispatchMessageInspector _messageInspector;
public MyMessageInspectionBehavior(IDispatchMessageInspector messageInspector)
{
_messageInspector = messageInspector;
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(_messageInspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
The Ninject Binding:
this.Bind<IDispatchMessageInspector>()
.To<MyMessageInspector>()
.WhenInjectedInto<IEndpointBehavior>();
The last step is the Configuration:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="MyCustomMessageInspector"
type="Ninject.Extensions.Wcf.BaseNinjectBehaviorExtensionElement+NinjectBehaviorExtensionElement`1[[MyNamespace.MyMessageInspectionBehavior, MyAssemblyName]], Ninject.Extensions.Wcf" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior>
<MyCustomMessageInspector />
</behavior>
</endpointBehaviors>
I have parked this for now, as there doesn't seem to be any solution. I am simply connecting to my context directly with a view to improving it in the future.
I am implementing IErrorHandler in order to centralize all of the error handling for my WCF service in one place. This works fairly well:
public class ServiceErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
// ..Log..
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// ..Provide fault..
}
}
Now, we're using Ninject to inject dependencies in the rest of the service, and I'd like to do the same here. Since WCF is constructing the objects based on my configuration, and I don't think I have any hooks into this process, I need to use property injection:
[Inject]
public ILoggingService Logger { get; set; }
However, this never seems to get injected. I tried using Ninject's MVC extensions to set ServiceErrorHandler to allow injection like a filter, but that didn't seem to do the trick. Is there a way to make this happen?
Late answer, but you can inject dependencies into IErrorHandler by creating your custom ServiceHost, let's say TestServiceHost.
In your TestServiceHost you need to do:
Implement constructor with IErrorHandler parameter.
Inside, create a private nested class named ErrorHandlerBehaviour*, which needs to implement both IServiceBehavior and IErrorHandler. It also must have constructor with IErrorHandler parameter.
Override OnStarting() method, where you will add ErrorHandlerBehaviour to service behaviours. All behaviours must be added before base.OnStarting().
*the idea came from Juval Lowy's example in book - "Programming WCF Services". More information about Faults and Error extensions you can find there.
Here is the working host console application. I don't use IoC there, just Pure DI, but you can easily resolve logger with any IoC you want:
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace ConsoleHost
{
class Program
{
static void Main(string[] args)
{
var logger = new DummyLogger();
var errorHandler = new TestErrorHandler(logger);
ServiceHost host = new TestServiceHost(errorHandler, typeof(TestService), new Uri("net.tcp://localhost:8002"));
host.Open();
Console.WriteLine("Press enter to exit");
Console.ReadKey();
}
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
string Test(int input);
}
public class TestService : ITestService
{
public string Test(int input)
{
throw new Exception("Test exception!");
}
}
public class TestErrorHandler : IErrorHandler
{
private ILogger Logger { get; }
public TestErrorHandler(ILogger logger)
{
Logger = logger;
}
public bool HandleError(Exception error)
{
Logger.Log(error.Message);
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException fe = new FaultException();
MessageFault message = fe.CreateMessageFault();
fault = Message.CreateMessage(version, message, null);
}
}
public class TestServiceHost : ServiceHost
{
private readonly IErrorHandler errorHandler;
public TestServiceHost(IErrorHandler errorHandler, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
this.errorHandler = errorHandler;
}
protected override void OnOpening()
{
Description.Behaviors.Add(new ErrorHandlerBehaviour(errorHandler));
base.OnOpening();
}
class ErrorHandlerBehaviour : IServiceBehavior, IErrorHandler
{
private readonly IErrorHandler errorHandler;
public ErrorHandlerBehaviour(IErrorHandler errorHandler)
{
this.errorHandler = errorHandler;
}
bool IErrorHandler.HandleError(Exception error)
{
return errorHandler.HandleError(error);
}
void IErrorHandler.ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
errorHandler.ProvideFault(error, version, ref fault);
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
channelDispatcher.ErrorHandlers.Add(this);
}
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
}
// Dummy logger
public interface ILogger
{
void Log(string input);
}
public class DummyLogger : ILogger
{
public void Log(string input) => Console.WriteLine(input);
}
}
And configuration:
<system.serviceModel>
<services>
<service name="ConsoleHost.TestService">
<endpoint address="net.tcp://localhost:8002/TestService"
binding="netTcpBinding"
contract="ConsoleHost.ITestService" />
</service>
</services>
</system.serviceModel>
Btw. Make sure you added System.Runtime.Serialization to your references
I'm trying to use Routing Service (http://msdn.microsoft.com/en-us/library/ee517422.aspx) to route the data from clients to other end points. I have multiple clients and the end point which is called from routing service is located at a third party.
I also need to Log every transaction passed through routing service into SQL Database.
The problem is I can't write custom code in routing service as it's working from configuration files. Given that I can't write custom code in these classes, how can I achieve this?
1) create a class library and sign it with strong key.
2)create RoutingServiceBehavior class this class will implement IServiceBehavior, IDispatchMessageInspector interfaces, the code for message interception will be in this class:
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace Services.RoutingServiceBehavior
{
public class RoutingServiceBehavior : IServiceBehavior, IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
// This is your envelop
string s = request.ToString();
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
}
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
}
}
3) Create RoutingServiceBehaviorElement class, this class will implement BehaviorExtensionElement interface:
using System;
using System.ServiceModel.Configuration;
namespace Services.RoutingServiceBehavior
{
public class RoutingServiceBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(RoutingServiceBehavior); }
}
protected override object CreateBehavior()
{
return new RoutingServiceBehavior();
}
}
}
4)Build your project.
5)Add your assembly to GAC.
6)Open machine.config and add the name of your assembly under <behaviorExtensions> it shall look like that:
<add name="RoutingServiceBehavior" type="Services.RoutingServiceBehavior.RoutingServiceBehaviorElement, Services.RoutingServiceBehavior, Version=1.0.0.0, Culture=neutral" />
7) add the name of your service behavior in your wcf service under <serviceDebug>
<RoutingServiceBehavior/>
8)Make sure that the assembly dlls are included in your WCF service.
I'm trying to integrate Castle Windsor 2.5.0.1 with WCF on IIS (on XP/windows 2008). but I see that only Castle Windsor 3.0.0 works fine with the Caste.Facilities.WCFIntegration. I'm literally pulling my hair out to find out how to do WCF+Castle 2.5.0.1. I googled a lot to find a working example but failed. Please guide me.
You can do this:
web.config wcf config:
<behaviors>
<serviceBehaviors>
<behavior name="defaultBehaviour">
<createWithIoC />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add
name="createWithIoC"
type="Namespace.CreateWithIocBehaviour, Assembly.Namespace" />
</behaviorExtensions>
</extensions>
public class CreateWithIocBehaviour : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new IocServiceProviderBehaviour();
}
public override Type BehaviorType
{
get { return typeof(IocServiceProviderBehaviour); }
}
}
public class IocServiceProviderBehaviour : IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
var cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider =
new IocServiceProvider(serviceDescription.ServiceType);
}
}
}
}
}
public class IocServiceProvider : IInstanceProvider
{
private readonly Type _serviceType;
public IocServiceProvider(Type serviceType)
{
_serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return IoC.Resolve<object>(_serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
IoC.Release(instance);
}
}