I am trying to attach additional information to a FaultException for a WCF service, and then access it in the client. I am using the IErrorHandler ProvideFault method as my service has many methods that it exposes and I want to define the error handling logic in one place.
I have defined a data class CoreFault:
[DataContract]
public class CoreFault
{
[DataMember]
public string Foo { get; set; }
}
And implemented the IErrorHandler interface to return a strongly typed FaultException:
public class ErrorHandler : IErrorHandler
{
public void ProvideFault(Exception error, MessageVersion version, ref Message message)
{
var faultException = new FaultException<CoreFault>(
new CoreFault
{
Foo = "Bar"
},
"An error occurred",
null);
var fault = faultException.CreateMessageFault();
message = Message.CreateMessage(
version,
fault,
faultException.Action);
}
}
My service is configured in code:
public static void Configure(ServiceConfiguration config)
{
(config.Description.Behaviors[0] as ServiceBehaviorAttribute).IncludeExceptionDetailInFaults = true;
config.Description.Behaviors.Add(new ErrorHandlingBehaviour());
}
I have breakpointed the code after creating the message, and it shows the following XML:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header/>
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-GB">An error occurred</faultstring>
<detail>
<CoreFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/EmployerPortal.Models.Faults">
<Foo>Bar</Foo>
</CoreFault>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
But in my client, the error that is thrown is FaultException and not FaultException<CoreFault>, and the extra information is lost.
try
{
// Call service here
...
}
catch (FaultException<CoreFault> e)
{
throw;
}
catch (FaultException e)
{
// This line is hit
throw;
}
How can I pass Foo to the client, so that it can access it when it handles the exception?
Related
I have implemented a custom IAuthorizationPolicy for a WCF service. It works fine, except than when the authorization fails, the client receives a meaningless error.
If I just return false from the Evaluate method, the client just receives this:
System.ServiceModel.FaultException: 'The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.'
If I throw a FaultException<MyCustomErrorDetails>, the client receives this:
System.ServiceModel.CommunicationException: 'An error occurred while receiving the HTTP response to http://localhost:9034/Service1.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.'
Which is even less helpful.
How can I return a meaningful error to the client, such as "authorization failed"?
Enabling detailed error messages in a WCF service can be done through behavior configuration:
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
Typically you would create a type which holds the fault information, for example:
[DataContract]
public class SecurityFault
{
private string operation;
private string problemType;
[DataMember]
public string Operation
{
get { return operation; }
set { operation = value; }
}
[DataMember]
public string ProblemType
{
get { return problemType; }
set { problemType = value; }
}
}
In the service contract you would need to decorate the operations with the fault contract, like so:
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract(typeof(SecurityFault))]
int Divide(int number1, int number2);
}
In this case you would throw the exception in the authorization policy, for example:
public class AuthorizationAlwaysFails : IAuthorizationPolicy
{
public ClaimSet Issuer => throw new NotImplementedException();
public string Id => Guid.NewGuid().ToString();
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
var sf = new SecurityFault();
sf.Operation = "Authorization";
sf.ProblemType = "Authorization failed";
throw new FaultException<SecurityFault>(sf);
}
}
Client applications can then catch the exception as follows:
public class Program
{
static void Main(string[] args)
{
var wcfClient = new MyService.ServiceClient();
try
{
var result = wcfClient.Divide(10, 5);
Console.WriteLine(result);
}
catch (FaultException<SecurityFault> securityFault)
{
Console.WriteLine(securityFault.Detail.Operation);
Console.WriteLine(securityFault.Detail.ProblemType);
}
Console.ReadLine();
}
}
I have a WCF service, that needs to convert a video. I need to call that method from my Xamarin app. If I call the regular method everything works as expected, but if I call the Async method, I get the error below.
I already set the IncludeExceptionDetailInFaults on my WCF service to true, to get the details of the error.
WCF:
The interface:
[ServiceContract]
public interface IConvert
{
[OperationContract]
bool ConvertVideo(string path);
}
The service:
public class ConvertService : IConvert
{
public bool ConvertVideo(string path)
{
Console.WriteLine("Converting video... wait 3 sec");
Thread.Sleep(3000);
Console.WriteLine("Video converted!");
Console.WriteLine(path);
return true;
}
}
Xamarin:
This works:
try
{
_Client.ConvertVideo(GetFullFtpPath());
}
catch (Exception e) { }
This throws an error:
try
{
_Client.ConvertVideoAsync(GetFullFtpPath());
}
catch (Exception e) { }
The error:
{System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]:
Error in deserializing body of request message for operation 'ConvertVideo'.
OperationFormatter encountered an invalid Message body.
Expected to find node type 'Element' with name 'ConvertVideo' and namespace 'http://tempuri.org/'.
Found node type 'Element' with name 'ConvertVideoAsync' and namespace 'http://tempuri.org/'
(Fault Detail is equal to Error in deserializing body of request message for operation 'ConvertVideo'.
OperationFormatter encountered an invalid Message body.
Expected to find node type 'Element' with name 'ConvertVideo' and namespace 'http://tempuri.org/'.
Found node type 'Element' with name 'ConvertVideoAsync' and namespace 'http://tempuri.org/').}
EDIT: This only happens on Xamarin. I have tried it with WPF and everything works fine there.
your _Client should have _Client.ConvertVideoCompleted. your code should be something like
try
{
_Client.ConvertVideoCompleted+= yourHandler;
_Client.ConvertVideo(GetFullFtpPath());
}
catch (Exception e) { }
refer to https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-call-wcf-service-operations-asynchronously
In my case, I have a WCF Service (MyService.svc). I also have a client application that is instantiating and consuming the service contract.
What is the best way to handle exceptions at the service level and "transmit" them over to the client in an orderly and self-describing way?
If I have an unhandled exception on the WCF service, it seems as though that bubbles back to the client application as a CommunicationException.
But what's the best way to throw an exception at the service-level and have that same exception transmitted to the client-level? Or if I don't handle an exception at the service-level (or just re throw it at the service-level) how can that get explicitly directed to the client?
Or is that not typically how this SOA would work? What's the "right way" here?
Thanks!
First, if you want to pass the exception over the protocol, you have to wrap it in a faultexception, otherwise you will get a server error.
Use the FaultContract attribute over methods to enable faultContract and define the message you want to pass using creating a Message contract:
public interface IMyService
{
[OperationContract]
[FaultContract(typeof(Message))]
void WCFOperation();
}
[DataContract(Namespace = "http://www.mycompany.pt/myservice")]
public class Message
{
String _code;
[DataMember]
public String Code
{
get { return _code; }
set { _code = value; }
}
String _text;
[DataMember]
public String Text
{
get { return _text; }
set { _text = value; }
}
}
To convert exceptions to FaultExceptions, i use the following helper:
class Helper
{
internal static System.ServiceModel.FaultException<Message> ConvertToSoapFault(MyException ex)
{
FaultCode fc = new FaultCode(ex.Code);
return new FaultException<Message>(new Message(){ Text= ex.Message, Code= ex.Code});
}
internal static System.ServiceModel.FaultException ConvertToSoapFault(Exception ex)
{
return new FaultException(ex.Message);
}
}
Finally, at the operationContract implementation, simple do this:
public void WCFOperation()
{
try
{
...
}
catch (Exception ex)
{
Helpers.publishError(ex);
throw Helpers.ConvertToSoapFault(ex);
}
}
I am developing a distributed application. In it, there are roles and sets of permissions that I must validate.
Is a good pratice to throw an exception, in per example, unauthorized access?
Or should I send some message back to the client?
On your service operation, you can specify a FaultContract that will serve both purposes like so:
[OperationContract]
[FaultContract(typeof(MyServiceFault))]
void MyServiceOperation();
Note that MyServiceFault must be marked with DataContract and DataMember attributes, in the same way you would a complex type:
[DataContract]
public class MyServiceFault
{
private string _message;
public MyServiceFault(string message)
{
_message = message;
}
[DataMember]
public string Message { get { return _message; } set { _message = value; } }
}
On the service-side, you are then able to:
throw new FaultException<MyServiceFault>(new MyServiceFault("Unauthorized Access"));
And on the client-side:
try
{
...
}
catch (FaultException<MyServiceFault> fault)
{
// fault.Detail.Message contains "Unauthorized Access"
}
Well, you can catch all exceptions in the WCF service implementations methods and rethrow them as FaultExceptions. By doing it this way, the exception will be rethrown on the client with a message of your choosing:
[OperationContract]
public List<Customer> GetAllCustomers()
{
try
{
... code to retrieve customers from datastore
}
catch (Exception ex)
{
// Log the exception including stacktrace
_log.Error(ex.ToString());
// No stacktrace to client, just message...
throw new FaultException(ex.Message);
}
}
To avoid having unexpected errors relayed back to the client, it's also a good practice to never throw Exception instances in code on the server-side. Instead create one or more of your own exception types and throw them. By doing so, you can distinguish between unexpected server processing errors and errors that are thrown due to invalid requests etc:
public List<Customer> GetAllCustomers()
{
try
{
... code to retrieve customers from datastore
}
catch (MyBaseException ex)
{
// This is an error thrown in code, don't bother logging it but relay
// the message to the client.
throw new FaultException(ex.Message);
}
catch (Exception ex)
{
// This is an unexpected error, we need log details for debugging
_log.Error(ex.ToString());
// and we don't want to reveal any details to the client
throw new FaultException("Server processing error!");
}
}
Throwing general Dot Net Exceptions would make the service client proxies and the server channel to go in faulted state if you are not using basicHTTPBinding ...To avoid that you should always throw FaultException from the service...
from you catch block just use:
throw new FaultException("Your message to the clients");
after 2 days of trying to find out why my service isn't working I finally find the cause. Everytime I try to throw a new FaultException<AuthenticationException>, the server isn't actually throwing this but catching it itself.
So what is happening is that when I throw the exception the server is crashing with an unhandled System.ServiceModel.FaultException1`.
Here is my custom exception class:
[DataContract]
public class AuthenticationException
{
private string validationError;
[DataMember]
public string ValidationError
{
set { validationError = value; }
get { return validationError; }
}
public AuthenticationException()
{
}
public AuthenticationException(string valError)
{
validationError = valError;
}
}
And my interface:
[ServiceContract]
public interface IAuthenticator
{
[OperationContract]
[FaultContract(typeof(AuthenticationException))]
Account authenticateApplication(string userName, string Password);
What can cause this?
Edit: This is how I am throwing the exception:
catch (Exception)
{
throw new FaultException<AuthenticationException>(new AuthenticationException("There was a general error during the process."), new FaultReason("Error"));
}
Maybe your IIS server is not configured to allow passing exception to the client.
Follow the step 2 "Enable detailed errors for remote clients." of this post