WCF Fault Exception not being propagated to client - wcf

I have a WCF service that defines a FaultContract:
[OperationContract]
[FaultContract(typeof(InvalidOperationException))]
[FaultContract(typeof(NotSupportedException))]
[FaultContract(typeof(Exception))]
GetSysIdsResponse GetSysIds(string id);
The WCF service catches an exceptional case (null pointer exception) and throws my FaultException:
try
{
....
} }
catch (InvalidOperationException ex)
{
// The specified DirectoryEntry is not a container
throw new FaultException<InvalidOperationException>(ex);
}
catch (NotSupportedException ex)
{
// Search is not supported by the provider that is being used
throw new FaultException<NotSupportedException>(ex);
}
catch (Exception ex)
{
throw new FaultException<Exception>(ex);
}
It throws the last one. The thing is that it never gets to the client. First o9f all, "Fault contracts are published as part of the service metadata." I do not see it in the client metadata after I add the Service Reference.
Here is the client code. It never hits the catch block catch (FaultException e)
It just says the FaultException is uncaught by the user. It does catch the CommunicationException. I don't know what I am doing wrong?
try
{
response = client.GetSysIds("sgentil");
}
catch (FaultException<Exception> e)
{
Console.WriteLine("FaultException<Exception>: " + e.Detail);
client.Abort();
return;
}
catch (FaultException e)
{
Console.WriteLine("FaultException: " + e.Message);
client.Abort();
return;
}
catch (CommunicationException ex)
{
Console.WriteLine("CommunicationsException");
client.Abort();
return;
}
** I tried the approach of the first answer and defined two exceptions:
[DataContract]
public class SystemFault
{
[DataMember]
public string SystemOperation { get; set; }
[DataMember]
public string SystemReason { get; set; }
[DataMember]
public string SystemMessage { get; set; }
}
[DataContract]
public class DatabaseFault
{
[DataMember]
public string DbOperation { get; set; }
[DataMember]
public string DbReason { get; set; }
[DataMember]
public string DbMessage { get; set; }
}
I then applied it:
[FaultContract(typeof(SystemFault))]
GetSysIdsResponse GetSysIds(string id);
Then threw it:
catch (Exception ex)
{
SystemFault sf = new SystemFault
{
SystemOperation = "GetSystemIds",
SystemMessage = "Exception while obtaining AD user properties",
SystemReason = ex.Message
};
throw new FaultException<SystemFault>(sf);
}
The client DOES now see the SystemFault type and has that metadata:
catch(FaultException<SystemFault> sf)
{
Console.WriteLine("SystemFault {0}: {1}\n{2}",
sf.Detail.SystemOperation, sf.Detail.SystemMessage,
sf.Detail.SystemReason);
}
Yet still execution stops in the server at the line:
throw new FaultException<SystemFault>(sf);
It says:
FaultException'1 not handled by user code
Now what?

In my case, if the Interface was not marked with the following attributes the response was empty:
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults = true)]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(Fault))]
You could mark your operation with those attributes in order to make the response display the fault details.

The problem is that you are specifying that your FaultContract is of type XXXException. This will not work I think, you must create a custom FaultContract of your own, for example:
[DataContract]
public class InitializationFault
{
public InitializationFault(Exception exc, string msg)
{
Exception = exc;
Message = msg;
}
[DataMember]
public Exception Exception { get; set; }
[DataMember]
public string Message { get; set; }
}
then your ServiceContract becomes:
[OperationContract]
[FaultContract(typeof(InitializationFault))]
//..more
GetSysIdsResponse GetSysIds(string id);
and your client side code becomes:
try
{
response = client.GetSysIds("sgentil");
}
catch (FaultException<InitializationFault> e)
{
Console.WriteLine("FaultException<InitializationFault>: " + e.Detail);
//more
}

Related

ASP.NET Core Interception with Castle.DinamicProxy doesn't throw Exception with Async Methods !!! How can I solve this?

I have been creating a project with Aspect Oriented Programming paradigm and
I have an "ExceptionLogAspect" class attribute which is used on business methods to log the errors throwing from them.
public class ExceptionLogAspect : MethodInterception
{
private readonly LoggerServiceBase _loggerServiceBase;
private static byte _risk;
public ExceptionLogAspect(Type loggerService, byte risk)
{
if (loggerService.BaseType != typeof(LoggerServiceBase))
{
throw new System.Exception(AspectMessages.WrongLoggerType);
}
_loggerServiceBase = (LoggerServiceBase)Activator.CreateInstance(loggerService);
_risk = risk;
}
protected override void OnException(IInvocation invocation, System.Exception e)
{
var logDetailWithException = GetLogDetail(invocation);
logDetailWithException.ExceptionMessage = e.Message;
_loggerServiceBase.Error(logDetailWithException);
}
}
This Aspect migrates MethodInterception class that I created with Castle.DinamicProxy package. And OnException method included by MethodInterception logs the exception data.
public abstract class MethodInterception:MethodInterceptionBaseAttribute
{
protected virtual void OnBefore(IInvocation invocation){}
protected virtual void OnAfter(IInvocation invocation){}
protected virtual void OnException(IInvocation invocation, System.Exception e){}
protected virtual void OnSuccess(IInvocation invocation){}
public override void Intercept(IInvocation invocation)
{
var isSuccess = true;
OnBefore(invocation);
try
{
invocation.Proceed();//Business Method works here.
}
catch (Exception e)
{
isSuccess = false;
OnException(invocation, e);
throw;
}
finally
{
if(isSuccess)
OnSuccess(invocation);
}
OnAfter(invocation);
}
}
When I run the code and try-catch block doesn't work for Exception. So catch block isn't called and no messages are logged.
If I turn the business method into a syncronous method, exception will be thrown and data will be logged.
How can I solve this asynchronous method problem?
I tried this solution, it works properly.
Intercept method has to be like this to make this process asynchronous.
Otherwise, this method doesn't work properly for async.
There are some other ways, for example Castle CoreAsync Interceptor, you can find it on Github or NuGet.
https://github.com/JSkimming/Castle.Core.AsyncInterceptor
public override void Intercept(IInvocation invocation)
{
var isSuccess = true;
OnBefore(invocation);
try
{
invocation.Proceed(); //Metodu çalıştır
if (invocation.ReturnValue is Task returnValueTask)
{
returnValueTask.GetAwaiter().GetResult();
}
if (invocation.ReturnValue is Task task && task.Exception != null)
{
throw task.Exception;
}
}
catch (Exception e)
{
isSuccess = false;
OnException(invocation, e);
throw;
}
finally
{
if (isSuccess)
OnSuccess(invocation);
}
OnAfter(invocation);
}

Unable to read FaultException from an asp.net core connected service

I'm trying to catch the FaultException on a WCF client.
the generated reference contract is:
[System.ServiceModel.OperationContractAttribute(Action="urn:wcfname#method", ReplyAction="*")]
[System.ServiceModel.FaultContractAttribute(typeof(Error[]), Action= "urn:wcfname#method", Name="errors")]
[System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(Response))]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(Request))]
service.response method(service.request request);
and the generated error class is:
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.2")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="wcf")]
public partial class Error
{
private string errorCodeField;
private string errorStringField;
private string subErrorCodeField;
private string offendingFieldField;
private System.DateTime timeStampField;
private bool timeStampFieldSpecified;
private string detailStringField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType="integer", Order=0)]
public string errorCode
{
get
{
return this.errorCodeField;
}
set
{
this.errorCodeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=1)]
public string errorString
{
get
{
return this.errorStringField;
}
set
{
this.errorStringField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(DataType="integer", Order=2)]
public string subErrorCode
{
get
{
return this.subErrorCodeField;
}
set
{
this.subErrorCodeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=3)]
public string offendingField
{
get
{
return this.offendingFieldField;
}
set
{
this.offendingFieldField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=4)]
public System.DateTime timeStamp
{
get
{
return this.timeStampField;
}
set
{
this.timeStampField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool timeStampSpecified
{
get
{
return this.timeStampFieldSpecified;
}
set
{
this.timeStampFieldSpecified = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=5)]
public string detailString
{
get
{
return this.detailStringField;
}
set
{
this.detailStringField = value;
}
}
}
the code to call the sercie is below:
try
{
//call the service method
}
catch (FaultException<Error[]> ex)
{
var errorElement = XElement.Parse(ex.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var xmlDetail = (string)errorElement;
}
catch (FaultException ex)
{
var errorElement = XElement.Parse(ex.CreateMessageFault().GetReaderAtDetailContents().ReadOuterXml());
var xmlDetail = (string)errorElement;
}
When theres a FaultExeption<Error[]> ex, it enters there, but the error is empty string.
If I comment that part, it enters on the FaultException ex, but again the error is empty string.
I'm using .netCore 3.1
Any advice is appreciated.
Throwing FaultException indicates that there is no problem with the channel. The exception was thrown by the service. WCF's FaultException message is too general to pinpoint a specific problem, so you can customize the exception message like this:
public class HomeService : IHomeService
{
public Student Get(string id)
{
try
{
//Here, of course, an exception will be thrown
var result = Convert.ToInt32(id) / Convert.ToInt32("0");
return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };
}
catch (Exception ex)
{
var reason = new FaultReason("Exception thrown information");
var code = new FaultCode("500");
var faultException = new FaultException(reason, code, "It is GET that throws the exception");
throw faultException;
}
}
}

WCF FaultException <T> (fault contract) - .NET Core 2.2

Is faultcontracts with FaultException< T > not supported in .NET Core 2.2?
I have a WCF service reference like so - this exact code is not tested, but simular works well in .NET Standard 4.6.2.
Server side:
[OperationContract]
[FaultContract(typeof(MyErrorType))]
[WebInvoke(
Method = "GET",
UriTemplate = "/GetData?foo={foo}",
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
ExpectedReturnType GetData(int foo);
[DataContract]
[Serializable]
public class MyErrorType
{
[DataMember]
public int ErrorCode { get; set; }
[DataMember]
public string Description { get; set; }
}
try {
return GetData(123);
}
catch (Exception e)
{
throw new FaultException<MyErrorType>(new MyErrorType() { ErrorCode = 20, Description = "Server side error message returning to client" });
}
Client side
try
{
_client.GetData(123);
}
catch (FaultException <MyErrorType> e)
{
// NEVER ENDS UP HERE IN .NET Core 2.2
// works fine in .NET Standard 4.6.2
Console.WriteLine(e.Details.ErrorCode);
Console.WriteLine(e.Details.Description);
throw e;
}
catch (Exception e)
{
throw e;
}
Stumbled over the same problem now myself. For now I did this workaround that seems to work for me.
catch (FaultException<WarehouseFault> faultException)
{
// FaultException is not supported in .Net Core as far as I know.
// So for now code is moved in general Exception handling below which seems to work.
throw new Exception(".Net Core now seem to support FaultException!");
}
catch (Exception e)
{
if (e.InnerException is FaultException<WarehouseFault> faultException) {
// Handle the fault exception here.
}
// Other exceptions
}

REST Web Service using Java and Jersey API: Unable to Read data from DB

I am creating a REST Web Service using Java and Jersey API. The basic REST service works fine,but when I add in a DB connection it gives me a Class Not Found Exception and a SQL Exception - No driver found. I have included the ojdbc6.jar file in the Eclipse build path. Using the same code if I create a Java application it runs fine.
I have added my code below. Can some one plz suggest something.
EDIT: I included the jar file in the WEB-INF lib directory. But when I try to execute the code I get the following error: HTTP Status 405 - Method Not Allowed
public class Note {
private int noteId;
private String content;
private Date createdDate;
public Note() {}
public Note(int noteId, String content, Date createdDate) {
this.noteId = noteId;
this.content = content;
this.createdDate = createdDate;
}
public int getNoteId() {
return noteId;
}
public void setNoteId(int noteId) {
this.noteId = noteId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
#Override
public String toString() {
return "Note [content=" + content + ", createdDate=" + createdDate
+ ", noteId=" + noteId + "]";
}
}
public class NoteDAO {
DatabaseAccess data;
Connection connection;
public NoteDAO()
{
try {
data = new DatabaseAccess();
connect();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void connect() throws SQLException
{
try
{
data.connect();
connection = data.connection;
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public Note getNoteById(int id)
{
PreparedStatement prepStmt = null;
try {
String cSQL = "SELECT * FROM NOTE WHERE NOTEID = 12 ";
prepStmt = connection.prepareStatement(cSQL);
prepStmt.setInt(1, id);
ResultSet result = prepStmt.executeQuery();
Note note = new Note();
while (result.next())
{
note.setNoteId(result.getInt(1));
note.setContent(result.getString(2));
note.setCreatedDate( (Date) new java.util.Date(result.getDate(3).getTime()));
}
return note;
} catch (SQLException e) {
e.printStackTrace();
prepStmt = null;
return null;
}
}
}
#Path("/notes")
public class Notes {
#Context
UriInfo uriInfo;
#Context
Request request;
NoteDAO dao = new NoteDAO();
#Path("{note}")
#GET
#Produces(MediaType.APPLICATION_XML)
public Note getNote(
#PathParam("note") String idStr) {
int id = Integer.parseInt(idStr);
Note note = dao.getNoteById(id);
if(note==null)
throw new RuntimeException("Get: Note with " + id + " not found");
return note;
}
public class DatabaseAccess {
Connection connection = null;
public void connect() throws SQLException
{
String DRIVER = "oracle.jdbc.driver.OracleDriver";
String URL = "jdbc:oracle:thin:#xx.xxx.xx.xxx:1521:XXXX";
String UserName = "username";
String Password = "password";
try
{
Class.forName(DRIVER);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
try
{
connection = DriverManager.getConnection(URL,UserName,Password);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public void disconnect() throws SQLException
{
connection.close();
}
}
If you are using datasources that are managed by the application server, you need to put the ojdbc6.jar library inside the lib folder of your application server.
On JBoss for example, it would be $JBOSS_HOME/server/default/lib.
This is required, because in such case, the datasource is being build when the server starts and independently from your application, which means the server cannot use your application JARs.
If, however, you are pooling the connections yourself, you need to make sure, that the ojdbc6.jar is inside the lib folder of your application WAR archive.

WCF FaultContract Fails with NamedPipe

I have a simple IPC mechanism that uses WCF and named pipes. My goal is to propagate exception details (including the stacktrace) to the client for logging purposes (the rest of the application logging is located on the client).
If I use the following code I am able to catch FaultException<Exception> on the client and see exception details:
Contract:
[ServiceContract]
public interface IService
{
[OperationContract]
[FaultContract(typeof(Exception))]
void DoSomething();
}
Implementation:
public class Service : IService
{
public void DoSomething()
{
try
{
ThisWillThrowAnException();
}
catch (Exception e)
{
throw new FaultException<Exception>(e);
}
}
}
Client:
public void CallServer()
{
try
{
proxy.DoSomething();
}
catch (FaultException<Exception> e)
{
Console.WriteLine("Caught fault exception!");
}
}
This works fine and I see the message printed on the console. However, if I want to use my own derived exception instead of the base Exception class, it fails.
Custom Exception:
[Serializable]
public class MyException : Exception
{
public MyException () { }
public MyException (string message) : base(message) { }
public MyException (string message, Exception inner) : base(message, inner) { }
protected MyException (
SerializationInfo info,
StreamingContext context)
: base(info, context) { }
}
Change the FaultContract on IService.DoSomething to
typeof(MyException).
Change the throw clause in Service to
new FaultException<MyException>(new MyException(e.Message, e);
Change the catch clause in the client to
catch (FaultException<MyException> e)
When I execute this, a CommunicationException is caught on the client with the error:
System.ServiceModel.CommunicationException: There was an error reading from the pipe: The pipe has been ended. (109, 0x6d).
The MyException class is in a shared library available to both the client and server.
This question is very similar to this question, but that did not help me.
I resolved this by writing my own fault DataContract which contained a serialized list of StackFrames.
Apparently this MSDN article is not exactly accurate?
http://msdn.microsoft.com/en-us/library/ff649840.aspx
[DataContract]
public class MyFault
{
[DataMember]
public string Message { get; set; }
[DataMember]
public IList<SerializableMiniStackFrame> StackTrace { get; set; }
public static MyFault CreateFault(Exception e)
{
MyFault fault = new MyFault();
fault.Message = e.Message;
fault.InitTrace(e);
return fault;
}
/// <summary>
/// Initializes the stack trace based on when the inner exception was thrown.
/// </summary>
/// <param name="inner">The inner exception.</param>
private void InitTrace(Exception inner)
{
StackTrace trace = new StackTrace(inner, true);
InitTrace(trace);
}
/// <summary>
/// Initializes the internal serializable stack frames based on the given
/// stack trace.
/// </summary>
/// <param name="stackTrace">The stack trace.</param>
private void InitTrace(StackTrace stackTrace)
{
// Create a new list of serializable frames.
this.StackTrace = new List<SerializableMiniStackFrame>();
// Iterate over each frame in the stack trace.
foreach (StackFrame frame in stackTrace.GetFrames())
{
string type = "";
Type declaringType = frame.GetMethod().DeclaringType;
if (null != declaringType)
{
type = declaringType.FullName;
}
MethodBase method = frame.GetMethod();
string methodName = method.Name;
string parameters = string.Empty;
string delimiter = string.Empty;
foreach (ParameterInfo parameter in method.GetParameters())
{
parameters += string.Format("{0}{1} {2}", delimiter, parameter.ParameterType.Name, parameter.Name);
delimiter = ", ";
}
string file = Path.GetFileName(frame.GetFileName());
int line = frame.GetFileLineNumber();
// Create a serializable frame and add it to the list.
SerializableMiniStackFrame miniFrame = new SerializableMiniStackFrame(type, methodName, parameters, file, line);
this.StackTrace.Add(miniFrame);
}
}
}
/// <summary>
/// This class encapsulates basic stack frame information into a serializable
/// object.
/// </summary>
[DataContract]
public class SerializableMiniStackFrame
{
public SerializableMiniStackFrame() { }
public SerializableMiniStackFrame(string type, string method, string parameters, string file, int line)
{
this.Type = type;
this.Method = method;
this.Parameters = parameters;
this.File = file;
this.Line = line;
}
[DataMember]
public string Type { get; set; }
[DataMember]
public string Method { get; set; }
[DataMember]
public string Parameters { get; set; }
[DataMember]
public string File { get; set; }
[DataMember]
public int Line { get; set; }
}