Hi I have a problem handling exceptions in wcf.
I have a service like this one:
[ServiceContract]
public interface IAddressService
{
[OperationContract]
[FaultContract(typeof(ExecuteCommandException))]
int SavePerson(string idApp, int idUser, Person person);
}
I am calling the SavePerson() on the service in the WCFTestClient utility.
The SavePerson() implementation is:
public int SavePerson(string idApp, int idUser, Person person)
{
try
{
this._savePersonCommand.Person = person;
this.ExecuteCommand(idUser, idApp, this._savePersonCommand);
return this._savePersonCommand.Person.Id;
}
catch (ExecuteCommandException ex)
{
throw new FaultException<ExecuteCommandException>(ex, new FaultReason("Error in 'SavePerson'"));
}
}
But I get this error:
Failed to invoke the service. Possible
causes: The service is offline or
inaccessible; the client-side
configuration does not match the
proxy; the existing proxy is invalid.
Refer to the stack trace for more
detail. You can try to recover by
starting a new proxy, restoring to
default configuration, or refreshing
the service.
if I change the SavePerson method and instead of:
catch (ExecuteCommandException ex)
{
throw new FaultException<ExecuteCommandException>(ex, new FaultReason("Error in 'SavePerson'"));
}
I do
catch(Exception)
{
throw;
}
I don't get the above error, but I only get the exception message and no inner exception.
What am I doing wrong?
When you define the fault contract:
[FaultContract(typeof(ExecuteCommandException))]
you must not specify an exception type. Instead, you specify a data contract of your choice to pass back any values that you deem necessary.
For example:
[DataContract]
public class ExecuteCommandInfo {
[DataMember]
public string Message;
}
[ServiceContract]
public interface IAddressService {
[OperationContract]
[FaultContract(typeof(ExecuteCommandInfo))]
int SavePerson(string idApp, int idUser, Person person);
}
catch (ExecuteCommandException ex) {
throw new FaultException<ExecuteCommandInfo>(new ExecuteCommandInfo { Message = ex.Message }, new FaultReason("Error in 'SavePerson'"));
}
Related
I apologize if this has been asked before, please link me to it. I am having a hard time finding discussion as to whether it is an acceptable practice to catch all internal server errors (500) in an API.
I have seen some arguments for it and some against it.
I've always heard that one should send a status instead of errors when possible. However, I do see how the 500 is semantically taking responsibility for a failed request and letting a client know that their request may not be at fault. But a status can convey that as well and then the 500 is just reserved for application container level errors not errors in the database or a class library.
Is there an accepted standard or is this an opinion topic?
e.g.
public HttpResponseMessage GetUserRoles()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, AuthorizationService.GetUserRoles());
}
catch (SqlException sqle)
{
// log the exception
return Request.CreateResponse(HttpStatusCode.BadRequest, "A data error occured. Contact IT Support.");
}
catch (Exception e)
{
// log the exception
return Request.CreateResponse(HttpStatusCode.BadRequest, "An error occured. Contact IT Support.");
}
}
A good practice is to always return the same structure
public interface IResponse<T>
{
MsgType MsgType { get; }
string Msg { get; }
T Result { get; }
string Origin { get; }
}
Then you can have a MasterApiController with these methods
[NonAction]
public IHttpActionResult ResponseOk<T>(T result)
{
return Ok<IResponse>(ResponseFactory.Create<T>(result, "", "Negocio"));
}
[NonAction]
public IHttpActionResult ResponseEx(Exception ex, string msg = "Un error ha ocurrido.")
{
return ResponseMessage(Request.CreateResponse<IResponse>(HttpStatusCode.InternalServerError, ResponseFactory.Create(msg, ex, "Negocio")));
}
And in your controller that inherits from the master one you call this
[HttpGet]
[Route("Api/Alumno/Search")]
public dynamic Search(string codigo, string nombre, string estado, int? curso, int? paralelo)
{
return ResponseOk<dynamic>(result);
}
For all your non controlled exceptions you can have an action filter and manage them
public class ErrorLoggingFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//Exception
}
}
I Want to use custome FaultException like this
[DataContract]
public class Fault
{
[DataMember]
public string Message { get; set; }
}
public class MyFault<T>:FaultException<T> where T : Fault
{
public MyFault(T faultClass)
: base(faultClass, new FaultReason(faultClass.Message))
{
}
}
...
throw new MyFault<Fault>(new Fault {Message = "hello"});
...
Interface
[OperationContract]
[FaultContract(typeof(Fault))]
string GetData2(int value);
but when it get to the client it's transform to FaultException
try
{
var res = serv.GetData2(1);
}
catch (MyFault<WcfService1Sample.Fault> ex)
{
label1.Text = ex.Reason.ToString(); //-> i want here
}
catch (FaultException<ServiceReference1.Fault> ex)
{
label1.Text = ex.Reason.ToString(); //-> catch here
}
catch (Exception)
{
throw;
}
can i catch custom fault on clint ?
or the WCF automatic convert it to FaultException
how do i make this issue work
thanks kfir
There are two issues, I think. First:
What shambulator said: Your custom Fault class must be
a DataContract, and use
DataMember to mark all properties that shall be serialized.
If this is done, then WCF will
translate a service-side throw new FaultException<MyFault>(new MyFault { ... }); into a message which contains your fault, including the data; and
translate this fault on the client-side to a FaultException<MyFault>.
Second:
This client-side FaultException<> is generated by WCF. Maybe one can register a custom Exception-Translator, but I have never used it, never needed it, and not found such a thing after a one-minute Google search. I'd recommend just going with the FaultException plus custom Fault-type.
One of the things I never understood about WCF is why no Exception message details are propagated back to the calling client when the server encounters an unhandled exception.
For example, if I have the following server code
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Server : IServer
{
public DTO GetDTO()
{
DTO dto = new DTO();
dto.dto = dto;
return dto;
}
}
public class DTO
{
public DTO dto;
}
[ServiceContract]
public interface IServer
{
[OperationContract]
DTO GetDTO();
}
I deliberately introduced an ObjectGraph to cause a serialization exception when the DTO object is returned.
If I have a client that calls this Server's GetDTO() method, I will get the following CommunicationException.
The socket connection was aborted. This could be caused by an error
processing your message or a receive timeout being exceeded by the
remote host, or an underlying network resource issue. Local socket
timeout was '00:00:58.9350000'.
Which is absolutely useless. It has no inner exception and not even the real exception message.
If you then use Microsoft Service TraceViewer, you will see the exception but you must turn on the Diagnostics tracing for this.
The exception message that should be sent back is
There was an error while trying to serialize parameter
http://tempuri.org/:GetDTOResult. The InnerException message was
'Object graph for type 'TestWCFLib.DTO' contains cycles and cannot be
serialized if reference tracking is disabled.'. Please see
InnerException for more details.
So can anybody tell me how get the right exception message show up on the client side? Obviously, setting IncludeExceptionDetailInFaults to true doesn't make a difference.
I think that it is by design that the server errors are not propogated to client. This is in general a practice to not expose server internals to clients as the main purpose of Client Server architecture is independence of server.
You can still achieve this by using Fault Exception
Decorate your service declaration with a fault contract
[ServiceContract]
public interface IServer
{
[OperationContract]
[FaultContract(typeof(MyApplicationFault))]
DTO GetDTO();
}
Then catch errors in servcie implementation and throw a fault exception.
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Server : IServer
{
public DTO GetDTO()
{
try
{
DTO dto = new DTO();
dto.dto = dto;
return dto;
}
catch (Exception ex)
{
MyApplicationFault fault = new MyApplicationFault(...);
throw new FaultException<MyApplicationFault>(fault);
}
}
}
And catch the exception in client
IServer proxy = ...; //Get proxy from somewhere
try
{
proxy.GetDTO();
}
catch (TimeoutException) { ... }
catch (FaultException<MyApplicationFault> myFault) {
MyApplicationFault detail = myFault.Detail;
//Do something with the actual fault
}
catch (FaultException otherFault) { ... }
catch (CommunicationException) { ... }
Hope this helps.
For a nice tutorial please see Code Project Tutorial on Fault Exception
Consider the following very basic WCF service implementation:
public enum TransactionStatus
{
Success = 0,
Error = 1
}
public class TransactionResponse
{
public TransactionStatus Status { get; set; }
public string Message { get; set; }
}
[ServiceContract]
[XmlSerializerFormat]
public interface ITestService
{
[OperationContract]
TransactionResponse DoSomething(string data);
}
public class TestService : ITestService
{
public TransactionResponse DoSomething(string data)
{
var result = ProcessData(data); // may throw InvalidOperationException
return new TransactionResponse()
{
Status = TransactionStatus.Success,
Message = result
};
}
private string ProcessData(string data)
{
if (data = "foobar")
throw new InvalidOperationException();
return data;
}
}
In the instance that the DoSomething method does throw an InvalidOperationException, I would like to intercept the fault and return a TransactionResponse object, rather than have WCF raise a FaultException with the client. How can I do this without surrounding each method body in a huge try catch statement? Is there some where I can hook into? Can I do this with some sort of attribute or something? An example of how I would like to handle it can be demonstrated using ASP.NET MVC:
public class ApiController : BaseController
{
protected override void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
var message = HttpContext.IsDebuggingEnabled ? ex.ToString() : ex.Message;
_logger.Error("Error processing request for controller {0}, action {1}",
filterContext.RequestContext.RouteData.Values["controller"],
filterContext.RequestContext.RouteData.Values["action"]);
_logger.Error(ex.ToString());
filterContext.ExceptionHandled = true;
filterContext.Result = ToXml(new ApiResult(false)
{
Message = message
});
}
// ...
}
Using the above method in MVC, I can ensure that no matter which controller action throws an exception, I can handle it and return an appropriately formatted ActionResult containing the necessary info. Is there a way to do this kind of thing with WCF?
Check out the WCF IErrorHandler interface - it allows you to centrally define one way in your service implementation to catch all exceptions and either swallow them, or convert them to WCF-friendly SOAP exceptions. This will make sure the channel between the client and the server isn't faulted, e.g. it can still be used after this call failed.
I don't understand why you'd want to "catch" the SOAP faults and convert those to something else, though.... nor do I know of any support that WCF would give you. The basic assumption is: catch .NET exceptions and convert them into interoperable SOAP faults
if i made my exception Serializable like this article from msdn , so can my exception serialized over WCF ?
Yes you can serialize exceptions and return them with WCF. I wrote an app where it was necessary for the WCF client to have the real exception that occurred on the server; not just a high level fault.
Here are the steps to implement what we have done:
1 - Declare a class EncodedException with a single string property
public class EncodedException
{
public string SerializedException {get;set;}
}
2 - On your service contract add the attribute to indicate that your service could return a FaultException exception.
[ServiceContract()]
public class MyService
{
[OperationContract]
[FaultContract(typeof(EncodedException),
ProtectionLevel = ProtectionLevel.EncryptAndSign)]
public string Method1 ();
}
3 - In you service implementation add a try/catch in all service operations:
public void Method1()
{
try
{
// some code here
}
catch( Exception ex)
{
EncodedException encodedException = Helper.SerializeException( ex );
throw new FaulException<EncodedException>();
}
}
4 - In your client code catch the exception and unwrap it:
public void someMethod()
{
try
{
serviceClient.Method1();
}
catch( FaulException<EncodedException> ex)
{
Exception decodedException = Helper.DeserializeException( ex );
throw new decodedException();
}
}
5 - Write the Helper to serialize/deserialize the exception. If you need help with that part too, let me know.
I hope this helps.
I don't see why not, if you've successfully created an object that can be serialized over classic asmx, then it be fine in WCF.