I have a WCF service that I call the following way:
MyService client = new MyService();
bool result = client.MyServiceMethod(param1, param2);
Variable result is set to true or false to indicate success or failure. In case of success it is clear but in case of failure I need to get some details on what failed.
From my service I use
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = HttpStatusCode.BadRequest;
response.StatusDescription = "Invalid parameter.";
return false;
My question is how do I retrieve the response description and is that the correct way to provide failure feedback?
IMO it is best to define a custom class that you then return from your method. This class would contain the details of any errors. You do this using DataContracts.
A simplified example might be something like this...
[ServiceContract]
public interface IMyContract
{
[OperationContract]
MyResult DoSomething();
}
[DataContract]
public class MyResult
{
[DataMember]
public bool IsSuccess { get; set; }
[DataMember]
public string ErrorDetails { get; set; }
}
public class MyService : IMyContract
{
public MyResult DoSomething()
{
try
{
return new MyResult { IsSuccess = true };
}
catch
{
return new MyResult { IsSuccess = false, ErrorDetails = "Bad things" };
}
}
}
EDIT: Including consuming code per comment.
var client = new MyService();
var results = client.DoSomething();
if (results.IsSuccess)
{
Console.WriteLine("It worked");
}
else
{
Console.WriteLine($"Oops: {results.ErrorDetails}");
}
Generally you communicate problems to the client(s) using SOAP MSDN:Faults. Special advantage of faults is that WCF will ensure that your channel stays open after receiving fault message. By default, the service does not send any information explaining what happened. WCF does not reveal details about what the service does internally. See MSDN:Specifying and Handling Faults in Contracts and Services for more details. Also see SO:What exception type should be thrown with a WCF Service?
For debugging purposes you can add ServiceDebug behavior and set IncludeExceptionDetailInFaults to true to get the stack trace (in non-production environments)
Related
In my .NET Core Web API project many controller endpoints have code like this example
public async Task<ActionResult<User>> UpdateUserUsernameAsync(/* DTOs */)
{
try
{
User user = null; // Update user here
return Ok(user);
}
catch (EntityNotFoundException entityNotFoundException) // map not found to 404
{
return NotFound(entityNotFoundException.Message);
}
catch (EntityAlreadyExistsException entityAlreadyExistsException) // map duplicate name to 409
{
return Conflict(entityAlreadyExistsException.Message);
}
catch (Exception exception) // map any other errors to 500
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
I would like to create a mapping for the controllers that catches exceptions and maps them to HTTP responses before sending them back to the client.
A similiar question has been asked 4 years ago
ASP.NET Core Web API exception handling
In NestJs it's possible to define your own mappings by extending a base class e.g.
export class MyCustomException extends HttpException {
constructor() {
super('My custom error message', HttpStatus.FORBIDDEN);
}
}
Taken from here https://docs.nestjs.com/exception-filters#custom-exceptions
So basically I want to define mapping classes that could look like this sample code (this just shows my pseudo implementation)
// Map MyCustomException to NotFound
public class MyCustomExceptionMapping<TCustomException> : IExceptionMapping<TCustomException>
{
public ActionResult Map(TCustomException exception)
{
return NotFound(exception.Message);
}
}
Next I can cleanup the controller endpoint method to
public async Task<ActionResult<User>> UpdateUserUsernameAsync(/* DTOs */)
{
User user = null; // Update user here
return Ok(user);
}
Whenever an exception gets thrown the API would try to find the correct mapping interface. Otherwise it sends back a 500.
It would be nice to define such mappings and avoid a huge switch case for every exception in your project.
Does something like this exists? Is the accepted answer from the previous question still up to date?
Use Exception Filters it will be called when the controller throws an Exception and you can define the custom response. Microsoft Docs
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
HttpStatusCode status;
var message = "";
var exceptionType = context.Exception.GetType();
if (exceptionType is EntityNotFoundException)
{
status = HttpStatusCode.NotFound;
message = context.Exception.Message;
}
else if (exceptionType is EntityAlreadyExistsException)
{
status = HttpStatusCode.Conflict;
message = context.Exception.Message;
}
else
{
status = HttpStatusCode.InternalServerError;
message = "Internal Server Error.";
}
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new ApiResponse { Message = message, Data = null });
}
}
To use the filter on all controllers you must register it in the ConfigureServices method in the Startup.cs
services.AddMvc(config =>
{
config.Filters.Add(typeof(MyExceptionFilter));
})
Scenario
I have a .NET Core 2.2 web API with an exception handling middleware. Whenever an exception occurs in the application (inside the MVC layer) it gets caught by the exception middleware and returned as an internal server error back to the frontend and logged to kibana.
The problem
This is all fine and well when things go wrong, but sometimes I want to notify the calling application of specifically what went wrong. I.e., "Could not find record in database!" or "Failed to convert this to that!"
My Solution
I've used application Exceptions (not great - I know) to piggy back off the error middleware to return this to the frontend. This has been working fine, but has created a lot of noise around the code by having to throw a whole bunch of exceptions. I'm not satisfied with this approach and convinced that there must be a better solution.
My application architecture: I'm following a traditional n-tier application layout being services (business logic) and repositories (DAL) all speaking to each other. I would preferably like to elegantly bubble up any issues back to the user in any of these layers.
I've been thinking about this for a while now and am not sure what the best way to go about it is. Any advice would be appreciated.
I use a kind of the operation result pattern (non-official pattern).
The principle is to return a new Type containing:
Whether the operation was a success.
The result of the operation if was successful.
Details about the Exception that caused the failure.
Consider the following class:
public class OperationResult
{
protected OperationResult()
{
this.Success = true;
}
protected OperationResult(string message)
{
this.Success = false;
this.FailureMessage = message;
}
protected OperationResult(Exception ex)
{
this.Success = false;
this.Exception = ex;
}
public bool Success { get; protected set; }
public string FailureMessage { get; protected set; }
public Exception Exception { get; protected set; }
public static OperationResult SuccessResult()
{
return new OperationResult();
}
public static OperationResult FailureResult(string message)
{
return new OperationResult(message);
}
public static OperationResult ExceptionResult(Exception ex)
{
return new OperationResult(ex);
}
public bool IsException()
{
return this.Exception != null;
}
}
Then you could easily adapt OperationResult or create a class that inherits from OperationResult, but uses a generic type parameter.
Some examples:
The Operation Result Pattern — A Simple Guide
Error Handling in SOLID C# .NET – The Operation Result Approach
As per the Microsoft's standards, it is ideal to use ProblemDetails object in case of 4xx/5xx exceptions -
Following is the customised RequestDelegate method which you can use in ApiExceptionHandler to handle exceptions.
public async Task RequestDelegate(HttpContext context)
{
var exception = context.Features.Get<IExceptionHandlerFeature>().Error;
var problemDetails = new ProblemDetails
{
Title = "An unexpected error occurred!",
Status = GetStatusCode(exception),
Detail = _env.IsDevelopment() ? exception.Message : "An unexpected error occurred!",
Instance = $"{Environment.MachineName}:{context.TraceIdentifier}:{Guid.NewGuid()}"
};
_logger.LogError($"Exception thrown. StatusCode: {problemDetails.Status}. Instance: {problemDetails.Instance}", exception);
context.Response.StatusCode = problemDetails.Status.Value;
context.Response.WriteJson(problemDetails, "application/problem + json");
await Task.CompletedTask;
}
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();
}
}
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 new in WCF. I'm starting to create a WCF project that save people's information in EF database.
I had lots of errors and fixed them by searching in various sites but recently I have been stopped by some errors and can't fix them.
My project is n-tier with Entity framework data model. There is 4 layer for server.
DAL(Contain EF dataModel)-->BLL(Class library for insert/update/Delete Functions)-->Service Layer(wcf class library)-->Host Layer(windows service)
I host my service in a Windows Service. The service binding is nettcpbinding. I configure service true so no problem here.
i have a Project with name "Common"(class library) that All 4 project access to it and as you know i create a code generator and pasted generated class to common.(Separate class model with DAL).
Now the cient has just (Host Layer) service reference.in client, I have all entity classes in service class.
The problem occurs when I want to insert object to data base. See Code below :
Server Side (This Is in BLL And Service Layer):
namespace BLL
{
public static class bz
{
public static class People
{
public static void myInsert(Common.People p, out bool Result)
{
Result = false;
Common.EFContainer cntx = new Common.EFContainer();
cntx.ContextOptions.ProxyCreationEnabled = false;
cntx.ContextOptions.LazyLoadingEnabled = false;
if (p.FirstName == "" || p.FirstName == null)
{
throw new Exception("Fill Fist Name") ;
}
// ... more validate
//-->I Do not use first or single etc in validating<--
try
{
cntx.Peoples.AddObject(p);
cntx.SaveChanges();
Result = true;
}
catch (Exception ex)
{
throw ex;
}
}
}
public static class Gender
{
public static List<Common.Gender> GetData()
{
Common.EFContainer cntx = new Common.EFContainer();
cntx.ContextOptions.ProxyCreationEnabled = false;
cntx.ContextOptions.LazyLoadingEnabled = false;
return cntx.Genders.ToList();
}
}
}
}
namespace ServiceLayer
{
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IService
{
[FaultContract(typeof(NormalExeption))]
[OperationContract]
bool AddToPeople(Common.People p);
[OperationContract]
List<Common.Gender> GetGenders();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = System.ServiceModel.ConcurrencyMode.Single)]
public class myService : IService
{
[OperationBehavior]
public bool AddToPeople(Common.People p)
{
try
{
BLL.bz.People.myInsert(p, out result);
return result;
}
catch (Exception ex)
{
var exep = new NormalExeption(ex.Message, ex.Source, 0, Icons.Error);
throw new FaultException<NormalExeption>(exep, new FaultReason(new FaultReasonText(ex.Message)));
}
}
[OperationBehavior]
public List<Common.Gender> GetGenders()
{
return BLL.bz.Gender.GetData();
}
}
}
Client side :
ServiceRef.People p = new ServiceRef.People();
ServiceRef.myServiceClient client=new ServiceRef.myServiceClient();
p.FirstName="S";
//... Fill Other Fields
p.Childs=new List<ServiceRef.Child>();
p.Childs.Add(new ServiceRef.Child(){FirstName="A"});
p.Gender=client.GetGenders.first();
//... --->No Error Happen Till Here Error Is After This in Service<---
try
{
client.AddToPeople(p);
}
Catch(FaultException fe)
{
messagebox.show(fe.Detail.Message);
}
My Problems :
Both errors occur in client side after I insert the people object.
When i use Poco Code Generator For My EF I receive this error:
Collection Was Out Of Fixed Size
This error occurs in poco classes definition.
When I use Self Tracking Code Generator For my EF I receive this error:
Sequence contains no elements
I think This error occurs in contex.addobject(p) and I don't use any Single Or First in my code that return this exception.
Some one help me - which code generator can I use that doesn't have these errors or how can I fix these errors? I have VS 2010.
Edit: The service works when the server send objects to client but when client inserts it fails. In the client service reference config I changed the collection type to:
System.Collection.Generic.List