How to configure WCF binding programmatically? - wcf

I need to implement following using code:
<basicHttpBinding>
<binding name="NewBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
Is there any samples available? I'm working on WCF REST service and register routes manually. Placing this config into configuration doesn't work. I'd like to get it setup programmatically if possible. Also, at what point in code I should do it?
EDIT:
My service Routed in Global.asax like so:
foreach (var account in cmc.Accounts.Where(aa => aa.IsActive).ToList())
{
RouteTable.Routes.Add(
new ServiceRoute(
account.AccountId + "/mobile", new MyServiceHostFactory(), typeof(MobileService)));
}
And I have my own ServiceHost
public class MyServiceHost : WebServiceHost
{
private readonly Type _serviceType;
private readonly CompositionContainer _container;
public MyServiceHost(Type serviceType, CompositionContainer container, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
_serviceType = serviceType;
_container = container;
}
protected override void OnOpening()
{
if (Description.Behaviors.Find<MyServiceBehavior>() == null)
{
Description.Behaviors.Add(new MyServiceBehavior(_serviceType, _container));
}
base.OnOpening();
}
}

For this specific case, the equivalent binding is the following:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

Related

Having two different topics within same windows server service bus namespace in WCF endpoint

I have two endpoints defined pointing to two different service bus topics. With same transportClientEndpointBehavior and on same service.
<endpointBehaviors>
<behavior name="securityBehavior">
<transportClientEndpointBehavior>
<tokenProvider>
<windowsAuthentication>
<stsUris>
<stsUri value="https://on-permises:9355/Namespace" />
</stsUris>
</windowsAuthentication>
</tokenProvider>
</transportClientEndpointBehavior>
</endpointBehaviors>
<customBinding>
<binding name="messagingBinding" >
<textMessageEncoding messageVersion="None" writeEncoding="utf-8" >
<readerQuotas maxStringContentLength="2147483647"/>
</textMessageEncoding>
<netMessagingTransport/>
</binding>
</customBinding>
<endpoint name="endpoint1"
address="sb://on-permises/Namespace/topic1"
listenUri="sb://on-permises/Namespace/topic1/subscriptions/sub"
binding="customBinding"
bindingConfiguration="messagingBinding"
contract="WCFService.IService1"
behaviorConfiguration="securityBehavior" />
<endpoint name="endpoint2"
address="sb://on-permises/Namespace/topic2"
listenUri="sb://on-permises/Namespace/topic2/subscriptions/sub"
binding="customBinding"
bindingConfiguration="messagingBinding"
contract="WCFService.IService2"
behaviorConfiguration="securityBehavior" />
Once i run the application, I get the error :
System.ArgumentException: The value could not be added to the collection, as the collection already contains an item of the same type: 'Microsoft.ServiceBus.TransportClientEndpointBehavior'. This collection only supports one instance of each type.
Parameter name: item
I tried by defining two different endpoint behaviors, but get the same error.
any help here will be helpful.
Found the work around solution, the issue was service host was trying to add these two service endpoints in WSDL/Metadata. which was not necessary. So with the help of ServiceMetadataContractBehavior (IContractBehavior ) stopped exposing the WSDL/Metadata .
Any better approach or correction please let me know.
public class DisableContractMetadata : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
/// Here ServiceMetadataContractBehavior type is derived from IContractBehavior
/// MetadataGenerationDisabled property of ServiceMetadataContractBehavior type = flase disables disables exposing WSDL
contractDescription.ContractBehaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
}
Note that the above answer is incomplete as it doesn't actually implement IContractBehavior.
As this is caused by hosting multiple services, you can also do it in the host:
ContractDescription contractDescription = ContractDescription.GetContract(typeof(MyService));
contractDescription.ContractBehaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });
Or, if you have multiple contracts on a type, use this:
private static void DisableMetadataGeneration(Type serviceType)
{
// prevent this error due to hosting multiple services:
// System.ArgumentException: The value could not be added to the collection, as the collection already contains an item of the same type: 'Microsoft.ServiceBus.TransportClientEndpointBehavior'. This collection only supports one instance of each type.
foreach (Type contractType in GetContracts(serviceType))
{
ContractDescription contractDescription = ContractDescription.GetContract(contractType, serviceType);
contractDescription.ContractBehaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });
}
}
private static Type[] GetContracts(Type serviceType)
{
System.Diagnostics.Debug.Assert(serviceType.IsClass);
Type[] interfaces = serviceType.GetInterfaces();
List<Type> contracts = new List<Type>();
foreach (Type type in interfaces)
{
if (type.GetCustomAttributes(typeof(ServiceContractAttribute), false).Any())
{
contracts.Add(type);
}
}
return contracts.ToArray();
}

Can a WCF REST Service support both Basic and Windows authentication?

I have a self hosted REST WCF Windows Service. I've got Basic Authentication working for the service, but I would like to also support Windows Authentication for clients which support it. Would I have to have a separate endpoint on a different port?
UPDATE: I've gotten something close to working in WCF 4.0. Here is the code, the issue I'm having now is that I can only seem to get NTLM working, which requires the user to type in their credentials, which nullifies any benefits to using Windows Auth.
I'm still not sure how to get Windows Authentication working, without requiring the user to enter their password in a second time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.IdentityModel.Selectors;
namespace BasicAndNegotiateAuth
{
class Program
{
static void Main(string[] args)
{
Uri newUri = new Uri(new Uri("http://localhost/"), "/");
WebServiceHost webHost = new WebServiceHost(typeof(HelloWorldService), newUri);
// TransportCredentialOnly means we can use http
WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic | HttpClientCredentialType.Ntlm;
ServiceEndpoint ep = webHost.AddServiceEndpoint(typeof(IHelloWorld), binding, newUri);
WebHttpBehavior wb = new WebHttpBehavior();
ep.EndpointBehaviors.Add(wb);
ep.Behaviors.Add(new WebHttpCors.CorsSupportBehavior());
//ServiceAuthenticationBehavior sab = null;
//sab = webHost.Description.Behaviors.Find<ServiceAuthenticationBehavior>();
//if (sab == null)
//{
// sab = new ServiceAuthenticationBehavior();
// sab.AuthenticationSchemes = AuthenticationSchemes.Basic | AuthenticationSchemes.IntegratedWindowsAuthentication;
// host.Description.Behaviors.Add(sab);
//}
webHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
webHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
webHost.Open();
Console.ReadLine();
}
}
public class CustomUserNameValidator: UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
int i = 1;
}
}
[ServiceContract]
public interface IHelloWorld
{
[System.ServiceModel.OperationContract]
[System.ServiceModel.Web.WebGet(
UriTemplate = "/",
ResponseFormat = WebMessageFormat.Json)]
string GetHello();
}
public class HelloWorldService : IHelloWorld
{
public string GetHello()
{
ServiceSecurityContext ssc = ServiceSecurityContext.Current;
return "Hello World";
}
}
}
In .NET 4.5, you can support multiple authentication schemes on a single endpoint in WCF.
Here is an example of how you would do it in code for a Self-Hosted Service:
ServiceAuthenticationBehavior sab = null;
sab = serviceHost.Description.Behaviors.Find<ServiceAuthenticationBehavior>();
if (sab == null)
{
sab = new ServiceAuthenticationBehavior();
sab.AuthenticationSchemes = AuthenticationSchemes.Basic |
AuthenticationSchemes.Negotiate | AuthenticationSchemes.Digest;
serviceHost.Description.Behaviors.Add(sab);
}
else
{
sab.AuthenticationSchemes = AuthenticationSchemes.Basic |
AuthenticationSchemes.Negotiate | AuthenticationSchemes.Digest;
}
Alternatively, you can set it up in your config file like this:
<behaviors>
<serviceBehaviors>
<behavior name="limitedAuthBehavior">
<serviceAuthenticationManager authenticationSchemes=
"Negotiate, Digest, Basic"/>
<!-- ... -->
</behavior>
</serviceBehaviors>
</behaviors>
Then specify InheritedFromHost in your binding settings like this:
<bindings>
<basicHttpBinding>
<binding name="secureBinding">
<security mode="Transport">
<transport clientCredentialType="InheritedFromHost" />
</security>
</binding>
</basicHttpBinding>
</bindings>
See this article on MSDN: Using Multiple Authentication Schemes with WCF.

Declaratively configure WCF behavior extension on programmatically constructed endpoint

I have a WCF behavior extension that I would like to add to a WCF client. However, the client is constructed programmatically. The endpoint address may vary, but I know the type. I could add the behavior programmatically or in the config file (preferred), but I need to pass some configuration in the config file only.
I don't want this in Common behaviors (machine.config).
I can add the behavior programmatically
endpoint.Behaviors.Add(new MyCustomBehavior())
But I'd rather do it in config, so I can configure the extension there as well.
Is it possible to declaratively add and configure an endpoint behavior extension to a programmatically constructed endpoint knowing only the the type or interface while leaving the client endpoint to be constructed programmatically?
<system.serviceModel>
<client>
<!-- Created programmatically -->
</client>
<extensions>
<behaviorExtensions>
<add name="MyCustomBehavior" type="namespace.CustomBehaviors", MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="MyCustomBehavior">
<MyCustomBehavior MyImportantBehaviorParam1="foo" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
Of course I could put the config in another section, and have my behavior read it there, but I'd rather use the WCF facilities if possible.
To do that you need to create a behavior configuration extension for your endpoint. For more information on how to do that, check https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility-behavior-configuration-extensions.
Update: I see your issue now. There's no direct way to add to an endpoint created via code a behavior declared in configuration. You can still do it, though, but you'll need to use some reflection to access the CreateBehavior method of the behavior configuration extension (the method is protected) to actually create the endpoint behavior to add it to the endpoint created via code. The code below shows how this can be done.
public class StackOverflow_10232385
{
public class MyCustomBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
public void Validate(ServiceEndpoint endpoint)
{
Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
}
}
public class MyCustomBehaviorExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(MyCustomBehavior); }
}
protected override object CreateBehavior()
{
return new MyCustomBehavior();
}
}
[ServiceContract]
public interface ITest
{
[OperationContract]
string Echo(string text);
}
public class Service : ITest
{
public string Echo(string text)
{
return text;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
var configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ServiceModelSectionGroup smsg = configuration.GetSectionGroup("system.serviceModel") as ServiceModelSectionGroup;
EndpointBehaviorElement endpointBehaviorElement = smsg.Behaviors.EndpointBehaviors["MyCustomBehavior_10232385"];
foreach (BehaviorExtensionElement behaviorElement in endpointBehaviorElement)
{
MethodInfo createBehaviorMethod = behaviorElement.GetType().GetMethod("CreateBehavior", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Type.EmptyTypes, null);
IEndpointBehavior behavior = createBehaviorMethod.Invoke(behaviorElement, new object[0]) as IEndpointBehavior;
endpoint.Behaviors.Add(behavior);
}
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
And the configuration for this code:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="myCustomBehavior_10232385" type="QuickCode1.StackOverflow_10232385+MyCustomBehaviorExtension, QuickCode1"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="MyCustomBehavior_10232385">
<myCustomBehavior_10232385/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>

WCF WebHttp, Service, Behaviors, Endpoints, Custom Formatter

I followed this guide for creating my custom formatter so I could use the Newtonsoft Json.NET for object serialization since the built in Microsoft one doesn't support cycles from parent/child relationships.
http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/03/wcf-extensibility-message-formatters.aspx
In his example he's manually creating his ServiceHost. I am leveraging Routes and the WebServiceFactory taught to me by this guide.
http://blogs.msdn.com/b/endpoint/archive/2010/01/06/introducing-wcf-webhttp-services-in-net-4.aspx
From what I can tell I just need to figure out a way to add the appropriate Behaviour to my Service endpoints. Any help in pointing me in the right direction would be appreciated.
Some code snippets below for ease of reference...
In my Global.asax
WebServiceHostFactory webServiceHostFactory = new WebServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute(Accounts.Route, webServiceHostFactory, typeof(Accounts)));
If my web.config
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat="Json"/>
</webHttpEndpoint>
</standardEndpoints>
In the Main function of his program
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITestService), new BasicHttpBinding(), "soap");
WebHttpBinding webBinding = new WebHttpBinding();
webBinding.ContentTypeMapper = new MyRawMapper();
host.AddServiceEndpoint(typeof(ITestService), webBinding, "json").Behaviors.Add(new NewtonsoftJsonBehavior());
To use the routes and get a reference to the service endpoint, you'll need your custom service host factory. It can do the same as the WebServiceHostFactory which you're currently using (simply return a reference to the WebServiceHost), but instead return a reference to your custom service host.
You can find more information about service host factories at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx.
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return base.CreateServiceHost(serviceType, baseAddresses);
}
class MyServiceHost : WebServiceHost
{
public MyServiceHost(Type serviceType, Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{ }
protected override void OnOpening()
{
base.OnOpening();
foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
{
CustomBinding custom = endpoint.Binding as CustomBinding;
if (custom != null)
{
custom = new CustomBinding(endpoint.Binding);
}
custom.Elements.Find<WebMessageEncodingBindingElement>().ContentTypeMapper = new MyRawMapper();
endpoint.Binding = custom;
endpoint.Behaviors.Add(new NewtonsoftJsonBehavior());
}
}
}
}

WCF Rest Webservice with stream

I read the following post with interest as it is an exact replica of the problem I am experiencing (and driving me insane)
"For request in operation UploadFile to be a stream the operation must have a single parameter whose type is Stream." -http://social.msdn.microsoft.com/Forums/en/wcf/thread/80cd26eb-b7a6-4db6-9e6e-ba65b3095267
I have pretty much followed all code/examples I have found and yet still cannot get around this error -
http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx
All I would like to achieve is to post an image(jpeg/png) from an android device using the standard filename/stream parameters.More than likely it is something simple that I have misconfigured, misunderstood or left out but I need to have a solution for proof of concept.
public interface IConXServer
{
[OperationContract]
[WebInvoke(UriTemplate = "UploadImage({fileName})", Method="POST")]
void UploadImage(string fileName, Stream imageStream);
}
public class ConXWCFServer : IConXServer
{
public void UploadImage(string fileName, Stream imageStream)
{
//implement image save
}
}
web.config settings
-->
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="webHttpEndpoint" helpEnabled="false"/>
</webHttpEndpoint>
</standardEndpoints>
<bindings>
<webHttpBinding>
<binding name="webHttpBinding" transferMode="Streamed"/>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceThrottling maxConcurrentCalls="2147483647" maxConcurrentSessions="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
Using vs2010 and IIS Express. If I comment out the above method all the others methods work and return data as well as the wsdl query
Regards and thanks in advance
Kern
You mention WSDL, which leads me to believe you're getting the error while trying to browse the metadata endpoint for the service. So, first off, WSDL and REST don't go together, so you shouldn't expect to use it at all for a REST interface. Forget the service metadata concept even exists in the REST world.
Next While it's true the REST's webHttpBinding supports parameters in front of the Stream body parameter, other bindings do not and there must either be a single Stream parameter or a message contract with headers and a stream body.
So, in the end, the problem is not with the REST webHttpBinding at all, I bet it works just fine. If it doesn't I would be absolutely shocked because you're not doing anything that shouldn't work in that department. The problem is that you're expecting the metadata endpoint to generate WSDL for the service contract you've defined and that's just not supported.
I do it this way and it'works.
Add Factory Class to webservice (WcfService2.ServiceFactory)
<%# ServiceHost Language="C#" Debug="true" Service="WcfService2.Service1" CodeBehind="Service1.svc.cs" Factory="WcfService2.ServiceFactory" %>
My Interface:
public interface IService1
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest, Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "UploadFile/{fileName}")]
void UploadFile(string fileName, Stream fileContent);
}
My Method:
public void UploadFile(string fileName, Stream fileContent)
{
var pathfile = "\\\\SERVER\\TravelsRequestFiles";
using (var fileStream = new FileStream(string.Concat(pathfile, "\\", fileName), FileMode.Create, FileAccess.Write))
{
fileContent.CopyTo(fileStream);
}
}
my FactoryClass:
public class ServiceFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyServiceHost(serviceType, baseAddresses);
}
class MyServiceHost : ServiceHost
{
public MyServiceHost(Type serviceType, Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void InitializeRuntime()
{
ServiceEndpoint endpoint = this.Description.Endpoints[0];
endpoint.Behaviors.Add(new EndpointBehaviors());
base.InitializeRuntime();
}
}
}
I added a EndpointBehaviors class, wich it find de operation UploadFile and remove its DataContractSerializerOperationBehavior, and then works!
public class EndpointBehaviors: IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
ContractDescription cd = endpoint.Contract;
foreach (DispatchOperation objDispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
if (objDispatchOperation.Name.Equals("UploadFile"))
{
OperationDescription myOperationDescription = cd.Operations.Find("UploadFile");
DataContractSerializerOperationBehavior serializerBehavior = myOperationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
myOperationDescription.Behaviors.Remove(serializerBehavior);
}
}
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void Validate(ServiceEndpoint endpoint)
{
BindingElementCollection elements = endpoint.Binding.CreateBindingElements();
WebMessageEncodingBindingElement webEncoder = elements.Find<WebMessageEncodingBindingElement>();
if (webEncoder == null)
{
throw new InvalidOperationException("This behavior must be used in an endpoint with the WebHttpBinding (or a custom binding with the WebMessageEncodingBindingElement).");
}
}
}