Receiving POST data in WCF Service - wcf

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.

Related

CORS on Self Hosted WCF Service

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.

Unable to Catch the FaultException thrown by WCF Service in SilverLight client Application

I am Calling a WCF Service Method from my Silverlight Application. Wcf Service is returning a Fault Exception on Failure. I am able to throw the fault exception from my WCF Service. But it is not receiving to my Silverlight Application(.xaml.cs). Instead I am getting an exception "Communication Exception was unhandled by User, the remote server returned an Error:NotFound"
in References.cs file(AutoGenerated File)
I am calling the WCF Service Method in my .Xaml.cs file like below
private void btnExecuteQuery_Click(object sender, RoutedEventArgs e)
{
try
{
objService.GetDataTableDataAsync(_DATABASENAME, strQuery);
objService.GetDataTableDataCompleted += new EventHandler<GetDataTableDataCompletedEventArgs>(objService_GetDataTableDataCompleted);
}
catch (FaultException<MyFaultException> ex)
{
lblErrorMessage.Content = "Please Enter a Valid SQL Query";
}
}
And I wrote my GetDataTableDataCompleted event as below
void objService_GetDataTableDataCompleted(object sender, GetDataTableDataCompletedEventArgse)
{
//code
}
Here is my Service Method
public IEnumerable<Dictionary<string, object>> GetDataTableData(string dataBaseName, string query)
{
try
{
IEnumerable<Dictionary<string, object>> objDictionary;
objDictionary = objOptimityDAL.GetDataForSelectQuery(dataBaseName, query);
return objDictionary;
}
catch (Exception ex)
{
MyFaultException fault = new MyFaultException();
fault.Reason = ex.Message.ToString();
throw new FaultException<MyFaultException>(fault, new FaultReason("Incorrect SQL Query"));
}
}
Here My WCf Service is interacting with Data Access Layer and throwing the Fault Exception Successfully but it is not receiving to my client Method, Instead I am getting an unhandled Exception like
"Communication Exception was unhandled by User, the remote server returned an Error:NotFound" in References.cs code shown below
public System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, object>> EndGetDataTableData(System.IAsyncResult result) {
object[] _args = new object[0];
System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, object>> _result = ((System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, object>>)(base.EndInvoke("GetDataTableData", _args, result)));
return _result;
}
Here is the Web.config of Wcf Service
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Below is my ServiceReferences.ClientConfig file
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647"
closeTimeout="01:00:00"
receiveTimeout="01:00:00"
sendTimeout="01:00:00">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:3132/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"
name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
</configuration>
Please Suggest me someway to catch the faultException in my SilverlightClient
Thanks in Advance
You should have chosen Silverlight Enabled WCF Service when you created your service first time. It would have created all the infrastructure for you.
But you can still add the necessary code manually to the WCF Service project.
SilverlightFaultBehavior.cs
/// <summary>
/// The behavior which enables FaultExceptions for Silverlight clients
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class SilverlightFaultBehaviorAttribute : Attribute, IServiceBehavior
{
private class SilverlightFaultEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new SilverlightFaultMessageInspector());
}
public void Validate(ServiceEndpoint endpoint)
{
}
private class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if ((reply != null) && reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
property.StatusCode = HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
endpoint.Behaviors.Add(new SilverlightFaultEndpointBehavior());
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Service1.cs
[SilverlightFaultBehaviorAttribute]
public class Service1 : IService1
{
...
}
And on the client you should check the e.Error property inside the callback function. Try/catch from your example will not work.
The Silverlight Client
objService.GetDataTableDataCompleted += (s, e) =>
{
if(e.Error != null) {
if (e.Error is FaultException) {
lblErrorMessage.Content = "Please Enter a Valid SQL Query";
}
// do something with other errors
}
else {
// success
}
};
objService.GetDataTableDataAsync(_DATABASEName, strQuery);

Unable to make IDispatchMessageInspector work

I need to play with request header in a REST, Json, WCF web service.
I create my IDispatchMessageInspector
public class HeaderInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int ind = request.Headers.FindHeader("xxxxx", "");
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
}
Then a endpointbehavio to attach the inspector to endpoints :
public class HeaderInspectorBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
HeaderInspector headerinsp = new HeaderInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new HeaderInspector());
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
And finally a BehaviorExtensionElement :
public class MyExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(HeaderInspectorBehavior); }
}
protected override object CreateBehavior()
{
return new HeaderInspectorBehavior();
}
}
Those classes being in the same file / namespace PDM.WebService
My config is :
<behaviors>
<endpointBehaviors>
<behavior name="RestBehavior">
<HeaderInspectorBehavior/>
<webHttp helpEnabled="true" defaultOutgoingResponseFormat="Json" faultExceptionEnabled="true" automaticFormatSelectionEnabled="false" />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="HeaderInspectorBehavior" type="PDM.WebService.MyExtension, PDM.WebService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
Everything compile well, at execution i can catch the execution of method "public override Type BehaviorType",
but no other methods of the code are fired after that (i sent request and i got response, the service respond well).
i set debug point in every methods nothing else execute ! (especially ApplyDispatchBehavior).
Can somebody point out what i'm missing ?
Refer CreateBehavior() is not invoked for a similar issue.
Ensure that name of your service element is corresponding to mynamespace.myservicename
The service will be providing correct response even if you don't have the correct service name; but the CreateBehavior() will be called only when you have the correct name for the service element.
Example
<service
name="WcfServiceApp001.Service1"
behaviorConfiguration="InternalPayrollBehavior">
<endpoint address="" binding="basicHttpBinding"
behaviorConfiguration="EndpointBehavior"
contract="WcfServiceApp001.IService1"
/>
</service>

How can I change the WCF Router Backup List Behavior with Custom Filter?

I'm trying to find a way to handle exception in WCF router, it means when for any reason router can not send message to primary endpoint (here, there is EndpointNotFoundException, ServerTooBusyException, or CommunicationObjectFaultedException) and goes and select an endpoint from backup list and send this message to backup endpoint. how can I get this internal exception in router service? because at this time I want to change the router configuration dynamically in memory and change the backup endpoint with primary endpoint. is it possible to do this with IErrorHandler? or is it possible to do this Custom Filter ? How I can change the backup behavior with Custom Filter?
this is a full working example to implement IErrorHandler fo wcf service. we can implement this for WCF Router and get the internal exception in router level and then make decision how we can change the configuration in the runtime.
[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");
}
}
}
}
}
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>
so in HandleError method we can handle this exception in proper way.

IErrorHandler doesn't seem to be handling my errors in WCF .. any ideas?

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.