any one can guide me how to enable SSL & Certificate in case WCF TCP binding. any idea.
var baseAddress = "localhost";
var factory = new DuplexChannelFactory<IMyWCFService>(new InstanceContext(SiteServer.Instance));
factory.Endpoint.Address = new EndpointAddress("net.tcp://{0}:8000/".Fmt(baseAddress));
var binding = new NetTcpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
factory.Endpoint.Binding = binding;
var u = factory.Credentials.UserName;
u.UserName = userName;
u.Password = password;
return factory.CreateChannel();
thanks
MessageCredentialType is an enum. You can set MessageCredentialType.Certificate and set certificate credentials. You should look at documentation of MessageCredentialType enum where you can find example of setting certificate credentials.
I used this example to verify that it works. whole program looked like
using System;
using System.ServiceModel;
namespace ConsoleApplication2
{
[ServiceContract(Namespace = "http://UE.ServiceModel.Samples")]
public interface ICalculator
{
[OperationContract(IsOneWay = false)]
double Add(double n1, double n2);
[OperationContract(IsOneWay = false)]
double Subtract(double n1, double n2);
[OperationContract(IsOneWay = false)]
double Multiply(double n1, double n2);
[OperationContract(IsOneWay = false)]
double Divide(double n1, double n2);
}
public class CalculatorService : ICalculator
{
public double Add(double n1, double n2)
{
double result = n1 + n2;
Console.WriteLine("Received Add({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Subtract(double n1, double n2)
{
double result = n1 - n2;
Console.WriteLine("Received Subtract({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Multiply(double n1, double n2)
{
double result = n1 * n2;
Console.WriteLine("Received Multiply({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
public double Divide(double n1, double n2)
{
double result = n1 / n2;
Console.WriteLine("Received Divide({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
}
public class Client : ClientBase<ICalculator>, ICalculator
{
public double Add(double n1, double n2)
{
return base.Channel.Add(n1, n2);
}
public double Subtract(double n1, double n2)
{
throw new NotImplementedException();
}
public double Multiply(double n1, double n2)
{
throw new NotImplementedException();
}
public double Divide(double n1, double n2)
{
throw new NotImplementedException();
}
}
internal class Program
{
private static void Main(string[] args)
{
ServiceHost myServiceHost = new ServiceHost(typeof(CalculatorService));
// Open the ServiceHostBase to create listeners and start listening for messages.
myServiceHost.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Client c = new Client();
var res = c.Add(1, 2);
Console.ReadLine();
}
}
}
My configuration file looked like this
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:8000/servicemodelsamples/service/calc" binding="netTcpBinding" contract="ConsoleApplication2.ICalculator" behaviorConfiguration="net" >
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
</client>
<services>
<service name="ConsoleApplication2.CalculatorService" behaviorConfiguration="service">
<endpoint address="net.tcp://localhost:8000/servicemodelsamples/service/calc" binding="netTcpBinding" contract="ConsoleApplication2.ICalculator" >
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8000/servicemodelsamples/service" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding>
<security mode="Message">
<message clientCredentialType="Certificate"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="service">
<serviceCredentials>
<serviceCertificate findValue="localhost" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="net">
<clientCredentials>
<clientCertificate findValue="localhost" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
It worked for me.
Related
I have WCF service class 'MusicServiceManager' with the constructor that takes the parameter of IAppAmbientState. I need to pass parameter value to MusicServiceManager constructor at time initializing Service Host. I don't want singleton implementation for IAppAmbientState.
Service Contract
[ServiceContract]
public interface IAlbumService
{
[OperationContract]
string TestMessage();
[OperationContract]
IEnumerable<AlbumData> GetAlbums();
[OperationContract(Name = "GetAlbumByID")]
AlbumData GetAlbum(Guid ID);
[OperationContract(Name = "GetAlbumByName")]
AlbumData GetAlbum(string name);
}
AppAmbientState
public class AppAmbientState : IAppAmbientState
{
public IContainer ServiceContainer { get; }
public AppAmbientState(
IContainer container // autofac DIC container
)
{
ServiceContainer = container;
}
}
MusicServiceManager
public class MusicServicesManager : IAlbumService
{
private readonly IAppAmbientState _appAmbientState;
public MusicServicesManager(IAppAmbientState appAmbientState)
{
this._appAmbientState= appAmbientState;
}
public AlbumData GetAlbum(Guid ID)
{
throw new NotImplementedException();
}
Service Host Configuration Class
public class ServiceHostConfiguration : IServiceHostConfiguration
{
private readonly ServiceHost hostMusicServiceManager;
public ServiceHostConfiguration()
{
var container = ContainerConfiguration.Configure();
AppAmbientState AppAmbientStateInstance = new AppAmbientState(container);
// need help here... MusicServiceManager(AppAmbientStateInstance)????
// ideally I don't want to define my base address in class
this.hostMusicServiceManager = new ServiceHost(typeof(MusicServicesManager));
}
public void InitializeService()
{
try
{
this.hostMusicServiceManager.Open();
Console.WriteLine("App Web Services Started. Press [Enter] To Exit ");
}
catch(Exception exp)
{
Console.WriteLine("App Web Services Could Not Start:: ", exp);
}
}
public void CloseService()
{
this.hostMusicServiceManager.Close();
}
}
Autofac DI Container
public static class ContainerConfiguration
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
//Application Configuration
builder.RegisterType<Application>().As<IApplication>();
builder.RegisterType<ServiceHostConfiguration>().As<IServiceHostConfiguration>();
builder.RegisterType<AppAmbientState>().As<IAppAmbientState>();
builder.RegisterType<MusicServicesManager>().As<IAlbumService>();
Here is my Demo:
This is the project directory:
Program.cs:
using Autofac;
using Autofac.Integration.Wcf;
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace AutofacTest
{
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>();
builder.RegisterType<Service>();
using (IContainer container = builder.Build())
{
ServiceHost host = new ServiceHost(typeof(Service));
host.AddDependencyInjectionBehavior<Service>(container);
host.Open();
Console.WriteLine("The host has been opened.");
Console.ReadLine();
host.Close();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public interface ILogger
{
}
public class Service : IService
{
private readonly ILogger logger;
public Service(ILogger logger)
{
this.logger = logger;
}
public string Test()
{
return "TEst1";
}
}
public class Logger : ILogger
{
}
}
App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<system.serviceModel>
<services>
<service name="AutofacTest.Service">
<endpoint address="" binding="basicHttpBinding" contract="AutofacTest.IService" name="TestServiceEndPoint" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" name="TestServiceMexEndPoint" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/TestService" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.0.0" newVersion="5.2.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Feel free to let me know if the problem persists.
I'm trying to implement CORS suppor into my WCF service.
I got some codes from
https://enable-cors.org/server_wcf.html
public class CustomHeaderMessageInspector : IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CustomHeaderMessageInspector (Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
But I'm getting error message on this line
public class CustomHeaderMessageInspector : IDispatchMessageInspector
ERROR: Classes can inherit only from other classes
How can i inherit IDispatchMessageInspector
Thanks
I have made a demo, wish it is useful to you.
Reference.
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
Server.
class Program
{
static void Main(string[] args)
{
using (ServiceHost sh = new ServiceHost(typeof(MyService)))
{
sh.Open();
Console.WriteLine("service is ready...");
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract(Namespace = "mydomain")]
public interface IService
{
[OperationContract]
[WebGet(ResponseFormat =WebMessageFormat.Json)]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return $"Hello, busy World,{DateTime.Now.ToShortTimeString()}";
}
}
public class CustomHeaderMessageInspector : IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CustomHeaderMessageInspector(Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string displayText = $"Server has received the following message:\n{request}\n";
Console.WriteLine(displayText);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
string displayText = $"Server has replied the following message:\n{reply}\n";
Console.WriteLine(displayText);
}
}
public class CustomContractBehaviorAttribute : BehaviorExtensionElement, IEndpointBehavior
{
public override Type BehaviorType => typeof(CustomContractBehaviorAttribute);
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>();
requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
}
public void Validate(ServiceEndpoint endpoint)
{
}
protected override object CreateBehavior()
{
return new CustomContractBehaviorAttribute();
}
}
App.config
<system.serviceModel>
<services>
<service name="Server3.MyService" behaviorConfiguration="mybahavior">
<endpoint address="" binding="webHttpBinding" contract="Server3.IService" behaviorConfiguration="rest"></endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost:5638"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="mybahavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="rest">
<webHttp />
<CorsBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="CorsBehavior" type="Server3.CustomContractBehaviorAttribute, Server3" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
Client.
<script>
$(function(){
$.ajax({
method:"Get",
url: "http://10.157.13.69:5638/sayhello",
dataType:"json",
success: function(data){
$("#main").html(data);
}
})
})
</script>
Result
Feel free to let me know if there is anything I can help with.
You should add
Dim method = httpRequest.Method
If method.ToLower() = "options" Then httpResponse.StatusCode = System.Net.HttpStatusCode.NoContent
to your example at the end of BeforeSendReply, otherwise at least POST requests won't work.
I thought over creation file storage with use WCF.I seach in Internet and find many examples and write his service:
namespace CTFileStorage
{
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
interface CTIFileStorageClass
{
[OperationContract]
CTFileName UploadStream(CTRemoteFileInfo faInfo);
}
[MessageContract]
public class CTRemoteFileInfo : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public string FileName;
[MessageHeader(MustUnderstand = true)]
public long Length;
[MessageBodyMember(Order = 1)]
public System.IO.Stream FileByteStream;
public void Dispose()
{
if (FileByteStream != null)
{
FileByteStream.Close();
FileByteStream = null;
}
}
}
[MessageContract]
public class CTFileName
{
[MessageHeader(MustUnderstand = true)]
public string FileName;
}
public class CTFileStorageClass : CTIFileStorageClass
{
public CTFileName UploadStream(CTRemoteFileInfo faFileInfo)
{
try
{
Guid guid = Guid.NewGuid();
FileStream targetStream = null;
Stream sourceStream = faFileInfo.FileByteStream;
string uploadFolder = #"D:\Storage\";
string[] paths=faFileInfo.FileName.Split('.');
string path=null;
if (paths.Length>=2)
{
path="."+paths[paths.Length-1];
}
string filePath = Path.Combine(uploadFolder, guid.ToString()+path);
using (targetStream = new FileStream(filePath, FileMode.Create,
FileAccess.Write, FileShare.None))
{
//read from the input stream in 65000 byte chunks
const int bufferLen = 65000;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
{
// save to output stream
targetStream.Write(buffer, 0, count);
Thread.Sleep(10);
}
targetStream.Close();
sourceStream.Close();
}
CTFileName fileName = new CTFileName() { FileName = guid.ToString() };
return fileName;
}
catch
{
return null;
}
}
}
}
WCF hosted on windows service.app.config service:`
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="binding" openTimeout="04:00:00" sendTimeout="04:00:00" receiveTimeout="04:00:00" closeTimeout="04:00:00"
allowCookies="true" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
messageEncoding="Mtom" textEncoding="utf-8" transferMode="Streamed"
useDefaultWebProxy="true">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<!-- This section is optional with the new configuration model
introduced in .NET Framework 4. -->
<service name="CTFileStorage.CTFileStorageClass"
behaviorConfiguration="CalculatorServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
</baseAddresses>
</host>
<!-- this endpoint is exposed at the base address provided by host: http://localhost:8000/ServiceModelSamples/service -->
<endpoint address=""
binding="basicHttpBinding" bindingConfiguration="binding"
contract="CTFileStorage.CTIFileStorageClass" />
<!-- the mex endpoint is exposed at http://localhost:8000/ServiceModelSamples/service/mex -->
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<serviceDebug includeExceptionDetailInFaults="False"/>
<serviceThrottling
maxConcurrentCalls="1000"
maxConcurrentSessions="1000"
maxConcurrentInstances="1000"
/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>`
For modeling load create client-console application:
static void Main(string[] args)
{
Thread thread = new Thread(new ParameterizedThreadStart(Copy));
thread.Start("D:\\toad10513\\toad10513.rar");
Thread thread1 = new Thread(new ParameterizedThreadStart(Copy));
thread1.Start("D:\\toad10513\\toad105131.rar");
Thread thread2 = new Thread(new ParameterizedThreadStart(Copy));
thread2.Start("D:\\toad10513\\toad105132.rar");
Thread thread3 = new Thread(new ParameterizedThreadStart(Copy));
thread3.Start("D:\\toad10513\\toad105133.rar");
Thread thread4 = new Thread(new ParameterizedThreadStart(Copy));
thread4.Start("D:\\toad10513\\toad105134.rar");
Thread thread5 = new Thread(new ParameterizedThreadStart(Copy));
thread5.Start("D:\\toad10513\\toad105135.rar");
}
private static void Copy(object faFile)
{
string s = faFile as string;
if (s != null)
{
StorageClient.Storage.CTIFileStorageClassClient client = new Storage.CTIFileStorageClassClient();
Stream sw = File.Open(s, FileMode.Open);
Console.WriteLine(faFile.ToString());
client.UploadStream(ref s, sw.Length, sw);
sw.Close();
}
}
}
}
This application upload in storage, In different threads files size 400 MB. If using one thread upload end good. But if start 6 threads, that work only 3 threads,others lag. And after any minutes work I get exception:"Connection socket failed".
I read many tutorials but I could not get this running for my project. I made few GET services which send data and they work great, but I have problems receiving the data. I would really appreciate it if someone could tell me where I fail, instead of posting some links. :)
When I try to call the service in my browser I get the error: Method not allowed. But I Think this is just the first error.
Here is my code.
First the code in Android where I call the service:
public class MainActivity extends Activity {
String SERVICE_URI = "http://10.0.2.2:51602/RestServiceImpl.svc";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HttpPost request = new HttpPost(SERVICE_URI + "/registerUser");
request.setHeader("Accept", "application/json");
request.setHeader("Content-type", "application/json");
try {
JSONStringer user = new JSONStringer()
.object()
.key("userInfo")
.object()
.key("Email").value("mail")
.key("Password").value("pass")
.key("Cauntry").value("country")
.key("UserName").value("username")
.endObject()
.endObject();
StringEntity entity = new StringEntity(user.toString());
request.setEntity(entity);
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
IRestServiceImpl.cs:
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "registerUser",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
void receiveData(String data);
}
RestServiceImpl.cs:
public class RestServiceImpl : IRestServiceImpl
{
public void receiveData(String data)
{
//some code
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="RestService.RestServiceImpl" behaviorConfiguration="ServiceBehaviour">
<endpoint address ="" binding="webHttpBinding" contract="RestService.IRestServiceImpl" behaviorConfiguration="web">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Your problem is that you're passing a JSON object to the operation, but the operation is expecting a JSON string. If I understand correctly the Java code, this is what you're sending:
{
"userInfo" : {
"Email" : "mail",
"Password" : "pass",
"Cauntry" : "country",
"UserName" : "username"
}
}
Which is not a JSON string.
There are a couple of things you can do. The first alternative is to modify the operation not to take a string, but to take a data contract which is equivalent to that object.
The code would look something like the one below. Notice that you should also change the body format to Bare (instead of Wrapped).
public class RequestData
{
public UserInfo userInfo { get; set; }
}
public class UserInfo
{
public string Email { get; set; }
public string Password { get; set; }
public string Cauntry { get; set; } // typo to match the OP
public string UserName { get; set; }
}
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "registerUser",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
void receiveData(RequestData data);
}
Another alternative, if the "schema" of the data changes every time, would be to take the input as a Stream (not a String). That way you should be able to accept basically any input. You may also need a content type mapper. You can find more information about this scenario at http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx.
Have been reading around on IErrorHandler and want to go the config route.
so, I have read the following in an attempt to implement it.
MSDN
Keyvan Nayyeri blog about the type defintion
Rory Primrose Blog
This is basically just the msdn example wrapped in a class that inherits IErrorHandler and IServiceBehaviour ... then this is wrapped in the Extension element that inherits from BehaviourExtensionElement to allegedly allow me to add the element into the web.config. What have i missed?
I have got it to compile and from the various errors i have fixed it seems like WCF is actually loading the error handler. My problem is that the exception that i am throwing to handle in the error handler doesn;t get the exception passed to it.
My service implementation simply calls a method on another class that throws ArgumentOutOfRangeException - however this exception never gets handled by the handler.
My web.config
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basic">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<extensions>
<behaviorExtensions>
<add name="customHttpBehavior"
type="ErrorHandlerTest.ErrorHandlerElement, ErrorHandlerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="exceptionHandlerBehaviour">
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
<customHttpBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="exceptionHandlerBehaviour" name="ErrorHandlerTest.Service1">
<endpoint binding="basicHttpBinding" bindingConfiguration="basic" contract="ErrorHandlerTest.IService1" />
</service>
</services>
Service Contract
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(GeneralInternalFault))]
string GetData(int value);
}
The ErrorHandler class
public class ErrorHandler : IErrorHandler , IServiceBehavior
{
public bool HandleError(Exception error)
{
Console.WriteLine("caught exception {0}:",error.Message );
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (fault!=null )
{
if (error is ArgumentOutOfRangeException )
{
var fe = new FaultException<GeneralInternalFault>(new GeneralInternalFault("general internal fault."));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
else
{
var fe = new FaultException<GeneralInternalFault>(new GeneralInternalFault(" the other general internal fault."));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler = new ErrorHandler();
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
if (channelDispatcher != null)
{
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
And the Behaviour Extension Element
public class ErrorHandlerElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ErrorHandler();
}
public override Type BehaviorType
{
get { return typeof(ErrorHandler); }
}
}
Here's a full working example:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(MyFault))]
string GetData(int value);
}
[DataContract]
public class MyFault
{
}
public class Service1 : IService1
{
public string GetData(int value)
{
throw new Exception("error");
}
}
public class MyErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
{
var vfc = new MyFault();
var fe = new FaultException<MyFault>(vfc);
var fault = fe.CreateMessageFault();
msg = Message.CreateMessage(version, fault, "http://ns");
}
}
public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
{
public override Type BehaviorType
{
get { return GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
private IErrorHandler GetInstance()
{
return new MyErrorHandler();
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandlerInstance = GetInstance();
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(errorHandlerInstance);
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
if (endpoint.Contract.Name.Equals("IMetadataExchange") &&
endpoint.Contract.Namespace.Equals("http://schemas.microsoft.com/2006/04/mex"))
continue;
foreach (OperationDescription description in endpoint.Contract.Operations)
{
if (description.Faults.Count == 0)
{
throw new InvalidOperationException("FaultContractAttribute not found on this method");
}
}
}
}
}
and web.config:
<system.serviceModel>
<services>
<service name="ToDD.Service1">
<endpoint address=""
binding="basicHttpBinding"
contract="ToDD.IService1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<errorHandler />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="errorHandler"
type="ToDD.ErrorHandlerExtension, ToDD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
You can see if the web.config is working and loading by adding a print or a breakpoint to the ApplyDispatchBehavior, and see if that gets printed/hit when the service first opens. So is it being loaded?
I'd add a print/breakpoint at ProvideFault, as well.