I'm consuming a clunky WCF server that occasionally throws various exceptions, and additionally returns some of its errors as string. I have no access to the server code at all.
I want to override the inner WCF-client request invocation method and handle all inner exceptions and hard-coded errors returned by the server and raise the Fault event if an error occurs, pseudo:
class MyClient : MyServiceSoapClient
{
protected override OnInvoke()
{
object result;
try
{
result = base.OnInvoke();
if(result == "Error")
{
//raise fault event
}
catch
{
//raise fault event
}
}
}
So that when I call myClient.GetHelloWorld(), it goes thru my overridden method.
How can this be achieved?
I know I don't have to use the generated client, but I don't want to re-implement all the contracts again, and I want to use the generated ClientBase subclass or at least its channel.
What I need is control over the inner request call method.
Update
I read this answer, and looks it's partially what I'm looking for, but I'm wondering if there is a way to attach an IErrorHandler to the consumer (client) code only, I want to add it to the ClientBase<TChannel> instance somehow.
Update
This article also looks very promising but it doesn't work. The applied attribute doesn't seem to take effect.
I can't find a way to add IServiceBehavior to the client side.
Update
I tried attaching an IErrorHandler via IEndpointBehavior.ApplyClientBehavior calling:
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers
.Add(new ErrorHandler());
}
(clientRuntime is a parameter), but exceptions are still thrown directly skipping MyErrorHandler.
ApplyDispatchBehavior isn't called at all.
Conclusion
I need to achieve two aspects:
Wrap all exceptions that might occur during the lifetime of a BaseClient<TChannel> and decide whether to handle them or throw them on. This should take care of all operation (the service I'm consuming exposes few dozens)
Parse all server-replies and throw exceptions for some of them, so they're forwarded as in statement 1.
You could use and modify the Exception Handling WCF Proxy Generator, more specifically, the base class that it uses. It's basic idea (check this description too) is to provide connection resilience by catching connection faults, and retrying the failed operation. As you can imagine, for this purpose it needs to be able to catch thrown exceptions, and also, it can inspect the result of calls.
The main functionality is given by the ExceptionHandlingProxyBase<T> base class, which you use instead of the ClientBase<T>. This base class has an Invoke method as follows, you'd need to modify that.
Simplified Invoke:
protected TResult Invoke<TResult>(string operationName, params object[] parameters)
{
this.Open();
MethodInfo methodInfo = GetMethod(operationName);
TResult result = default(TResult);
try
{
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait);
result = (TResult)methodInfo.Invoke(m_channel, parameters);
}
catch (TargetInvocationException targetEx) // Invoke() always throws this type
{
CommunicationException commEx = targetEx.InnerException as CommunicationException;
if (commEx == null)
{
throw targetEx.InnerException; // not a communication exception, throw it
}
FaultException faultEx = commEx as FaultException;
if (faultEx != null)
{
throw targetEx.InnerException; // the service threw a fault, throw it
}
//... Retry logic
}
return result;
}
You'll need to modify the throw targetEx.InnerException; part to handle the exceptions as you need, and obviously the resturn value shoudl also be inspected for your needs. Other then that you can leave the retry logic or throw it away if you don't expect connection problems. There is another variant of the Invoke for void return methods.
Oh, and by the way, it works with duplex channels as well, there is another base class for those.
If you don't want to use the generator (it might not even work in newer versions of VS), then you could just take the base class for example from here, and generate the actual implementation class with T4 from your service interface.
If the service isn't returning a true exception, but just a message, you probably want to add a ClientMessageInspector as a new client behavior. Please see: https://msdn.microsoft.com/en-us/library/ms733786.aspx
I've ended up using something based on the answers in this question.
It sticks to the generated client code, and allows invocation of the operations generically.
The code is incomplete, feel free to fork and edit it. Please notify me if you found any bugs or made any updates.
It's pretty bulky so I'll just share the usage code:
using (var proxy = new ClientProxy<MyServiceSoapClientChannel, MyServiceSoapChannel>())
{
client.Exception += (sender, eventArgs) =>
{
//All the exceptions will get here, can be customized by overriding ClientProxy.
Console.WriteLine($#"A '{eventArgs.Exception.GetType()}' occurred
during operation '{eventArgs.Operation.Method.Name}'.");
eventArgs.Handled = true;
};
client.Invoke(client.Client.MyOperation, "arg1", "arg2");
}
Related
I'm working on a pretty simple CRUD controller in ASP.NET core and now I'm facing the issue, that I need to handle errors like working with an not existing entity (returning a 404) or creating a duplicate (returning a 400).
So now I'm wondering what is the most idiomatic way in ASP.NET Core to fetch expected errors and return the correct status code for them.
One way could be to throw exceptions in the services that are responsible for the actual CRUD and catch them inside of the controller:
/// Inside service...
public void Create(Entity entityDetails) {
if (entityAlreadyExists(entityDetails)) {
throw new EntityDuplicateException();
}
// ...
}
/// Inside controller...
[HttpPost("{operatorClientId}")]
public void CreateEntity(Entity entityDetails) {
try {
_entityService.CreateEntity(entityDetails);
return Ok();
} catch (EntityDuplicateException e) { // Some self defined exception type
return BadRequest(/* Some details about the entity */);
}
}
This seems a bit repetitive to me because a lot of code would be always the same, but it makes clear what to expect from the API at the first glance. I think this could also get a bit messy when more exceptions need to be handled.
Another approach could be to implement a method for UseExceptionHandler, where all exception types are handled like:
switch (exceptionHandlerPathFeature.Error)
{
case EntityDuplicateException e:
context.Response.StatusCode = 400;
// More details to response here
break;
case EntityNotFoundException e:
context.Response.StatusCode = 404;
// More details to response here
break;
case {} e:
context.Response.StatusCode = 500;
// More details to response here
break;
}
This would be a single place for the handling of expected (and unexpected) exceptions. But you cannot see at first glance what the controller will return. And it can get harder to obtain all required information for a meaningful error message in the response (in the controller every information is directly available, with the ExceptionHandler I need to wrap all information into the exception).
An exception, as its name applies, should only be used in exceptional circumstances, not for control flow. An entity not being found or already existing is not exceptional; it's a common case that you should be handling explicitly in your data-access code. On the other hand, a deadlock in your database is.
Instead of having your data-access methods throw exceptions, make them return something like a Tuple of (bool success, string errorMessage). In the controller, check the success member - if it's false, return the errorMessage to the caller.
As for your global exception handler, leave it generic. It should do little more than log all exceptions to a central location, so that you can inspect those logs regularly to determine whether your app really is encountering exceptional circumstances on a regular basis - in which case you need to fix it.
I am new to building API's. My project contains three typical layers: controllers, services responsible for business logic, and repositories which are accessing data. Every request coming to my controllers have to go through some validation process before a specific action is performed. For an example, please inspect the code below:
[HttpPost]
public async Task<ActionResult<TicketDTO>> CreateTicketAsync([FromBody] CreateTicketDTO ticket)
{
try
{
if (ticket.Events == null)
{
return BadRequest(new {Message = _localizer["LackOfEventsErrorMessage"].Value});
}
var user = await _userService.GetUserByIdAsync(ticket.UserId);
if (user == null)
{
return NotFound(new { Message = _localizer["UserNotFoundErrorMessage", ticket.UserId].Value });
}
var invalidTicket = await _ticketService.CheckHasUserPlayedAnyOfGamesBeforeAsync(ticket);
if (invalidTicket)
{
return BadRequest(new { Message = _localizer["EventsRepeatedByUserErrorMessage"].Value });
}
var createdTicket = await _ticketService.AddTicketAsync(ticket);
if (createdTicket == null)
{
return BadRequest(new { Message = _localizer["TicketNotCreatedErrorMessage"].Value });
}
return CreatedAtAction(nameof(GetTicketById), new {ticketId = createdTicket.TicketId}, createdTicket);
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError,
new
{
Message = ex.InnerException != null
? $"{ex.Message} {ex.InnerException.Message}"
: ex.Message
});
}
}
This is one of my controller methods. Before the ticket is saved to database, it has to pass few checks. The owner of the ticket must exist, if not i return user not found etc. The problem is I do not really like this way of validating requests. The method is messy, and not very readable. I would like to know what is a good approach to validate requests, and react properly if something wents wrong (for example return "UserNotFoundErrorMessage" if there is no user in a database, etc. single catch block doesn't solve my problem. I wouldn't also like to have multiple catch blocks there, it's also messy i think. Am i wrong?)
I wonder does the attached snippet violate some clean code rules or not? How the code should look like? What I am doing wrong?
All of this logic should be shuffled into your business layer, i.e. your services. The service methods, then, should return a "result" class, which is basically just a way of sending multiple bits of information back as the return, i.e. success/fail status, errors, if any, the actual result in the case of a query or something, etc. Then, you can simply switch on the error and respond accordingly.
As far as the catches go, especially the main one that simply returns a 500, use a global exception handler. Let the error bubble up from the action and rely on the global handler to return an appropriate response.
Like others have already pointed out, this does not seem half bad.
I can tell you as much that we have snippets of code that are 10 times the size of this. Tbh, this seems like small part compared to some modules i've seen in my company's codebase.
That being said, you could move a bit more logic away from the controller, and to other layers. For example, when getting a user by its Id, you can also throw a not found exception from your serviceclass if an user by that id does not exist. you have now stuffed everything into a controller, whilst it feels like this is more the resposibility of the service.
Another thing you could do is perhaps use middleware:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1
You can create validation pipelines for your response.
I've also tried working with a validation pattern. In this case i would create some rules for checks, and applied these rules to stuff to validate. I then had a validator object that would take all the rules an prompt an appropriate output. This made the code cleaner and resuse better, however added some complexity and i ended up not using it. It was different than the rest of the codebase and therefore foreign to colleagues, and thus i had good argument not to use it.
Is there a way of correctly handling WebExceptions within a Data Access Layer?
Below is a method SendReceive within our DAL used to communicate with our remote server, if there is a communication issue, such as endpoint being inaccessible and therefore no data can be retrieved, I would like the user to be redirected to a View, informing the user to please try again later.
private static TResult SendReceive<TResult, TPayLoad>(string method, string route, TPayLoad payload, bool post, bool authentication, string hashedPassword)
{
var subject = "WebApplication1 - " + method + " Error";
using (var webClient = new WebClient())
{
try
{
var uri = new Uri("http://ourdomain/ourwebapicontroller/" + route);
webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
if (authentication)
{
var hashedPasswordAsBytes = Encoding.UTF8.GetBytes(hashedPassword);
webClient.Headers.Add(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(hashedPasswordAsBytes));
}
var response = post ? webClient.UploadString(uri, JsonConvert.SerializeObject(payload)) : webClient.DownloadString(uri);
var parsedResponse = JsonConvert.DeserializeObject<TResult>(response);
return parsedResponse;
}
catch (WebException webException)
{
SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>WebException [" + webException.Message + "]</p>");
// Issue with endpoint
}
catch (Exception exception)
{
SendEmail(subject, MvcApplication.To, MvcApplication.From, "<p>Exception [" + exception.Message + "]</p>");
}
}
return default(TResult);
}
public Models.WebApplication1.Test GetTest(int id)
{
return SendReceive<Models.WebApplication1.Test, int?>("GetTest", "get-test/" + id, null, false, false, null);
}
public int SetTest(Models.WebApplication1.Test test)
{
return SendReceive<int, Models.WebApplication1.Test>("SetTest", "set-test", test, true, false, null);
}
As the DAL is referenced from a Controller I don't believe it is possible to use throw new HttpException(), this can however be handled like so:
public ViewResult Test(int id)
{
var test = Dal.GetTest(id);
if (test == null)
{
throw new HttpException(404, "Please try again later.");
}
return View(test);
}
Would prefer to handle the communication issue within SendReceive as opposed to handling at Controller level for each method referencing SendReceive.
Everything depends on what you mean by "handle" and even "exception."
Controller
Within the controller, what do you want to do if the client requests something that doesn't exist? A 404 is a good response. But what if the DAL throws an exception? Would it make sense to return the exact same result to the client? A 500 error which tells the client something went wrong might make more sense.
That mismatch is indicated here:
throw new HttpException(404, "Please try again later.");
If the request threw an exception (for any reason, including the DAL) then returning a 500 error with "try again later" makes sense. You're communicating clearly that the problem is on your end. Sorry, hopefully it won't happen again, and if does we're working on it.
If the client requested something that doesn't exist then that may or may not ever change. Should they try again later? Why? Maybe what they've requested will never be found. That's also not an exception. Someone asking for something that doesn't exist and getting nothing means that your application is working correctly. The 404 tells them that our application is working - we just don't have what they want.
Based on that, bubbling up an actual exception to the controller probably makes sense. The DAL doesn't know about the controller or even a website. It's not in a good position to know whether or not the caller should know that there was an exception.
DAL
"Handling" an exception can mean different things. (I'll leave out my opinion about which is right because it's not relevant.)
If your DAL throws an exception, you can do a few things. Some are maybe better than others, but again, that depends on opinion and needs.
- Do nothing. Let the exception bubble up.
- Log it and rethrow it.
- Log it then wrap it in another exception that provides some context, and throw the new exception. (Whether to wrap an exception or not is a whole discussion.)
Some would say that "handling" an exception is something different that involves somehow reacting to the exception in a way that solves a problem, something we're less likely to do. For example, if our application retrieves a daily Chuck Norris joke from an API but it throws an exception, we might log it so we know something went wrong but then replace it with a backup Chuck Norris joke.
The most important thing I wouldn't do is "hide" the exception so that, to the caller, an exception and "nothing found" look the same. If something has gone wrong, the controller needs to know that - even if it doesn't understand the specifics - so it (not the DAL) - can determine what should be communicated to the caller.
The relationship between the controller and the DAL is similar to that between the browser client and the controller. If it's not just working, we communicate that. If there's no data, we communicate that.
I don't recommend putting writing code in the DAL that sends an email. That's very specific, and it couples all of your code to that decision and possibly to an implementation of sending mail.
An alternative is defining an interface like this:
public interface ILog
{
void LogException(Exception ex);
void LogMessage(string message);
}
...and injecting into the DAL class. When an exception occurs, call _log.LogException(ex);. Your DAL doesn't know what the implementation is. You could log it or even send an email if you want to.
I'm writing a WCF Service, and was wondering if there is a recommended pattern for returning various messages from a service method.
For example, if I call the public User Login(string userName, string passWord) method in my service, I want it to return my user object when they are valid users. However, if the details are not correct, or the account is disabled, I want to be able to return that data to the client.
I initially thought I would throw a LogonFailureException (custom exception class) when one of these two scenarios occurred. In the client, I could then catch this type and process it as I wanted. However, if I'm debugging the application, it breaks when the exception is thrown (even if I wrap it in a FaultException).
I could also create a ServiceResponse class that all my methods use as a return type - in there I could then have a "Messages" collection, but that seems potentially counter-intuitive. I'd rather the returntypes of my methods were simply what I expect to get back in normal circumstances.
So, bottom line, is there a particular pattern of dealing with messages coming back from the service method that is recommended?
Thanks
You need to throw a strongly-typed soap fault:
[DataContract]
public class ConnectionFault
{
[DataMember]
public string Issue { get; set; }
[DataMember]
public string Details { get; set; }
}
[FaultContract(typeof(ConnectionFault))]
[FaultContract(typeof(DataReaderFault))]
[OperationContract]
Int16 GetInStock(int productId);
//when it's time to throw the exception
var connectionFault = new ConnectionFault();
connectionFault.Issue = "Problem connecting to the database";
connectionFault.Details = ex.Message;
throw new FaultException<ConnectionFault>(connectionFault);
Then the client can catch it.
In my opinion, There are 2 scenarios.
1. You wish to throw a sort of exception on the Service side.
2. You wish to return a class that contains error data of something went wrong in Service.
What is the difference ? The first would be just like yaron wrote, you throw and catch a FaultException.
The second would be just to return an object, for example if something went wrong in your service, you always return ErrorData - this is a data contract that contains message and exception stack as string - you can add ErrorData.Update method, and every time you catch a new exception in service, update the ErrorData message.. this is good for long running calls that might catch several exception but you dont want to abort on the first one.
No matter which of these you choose, DO NOT INHERIT from Exception, I have read this is bad practice, you dont want to serialize and transfer Exception based data contracts over WCF.
I've a few web methods that I use to call some external services like the Google Calendar API, obviously these can be extremely brittle.
Unfortunately I now realise that any error thrown on these methods are not causing an exception to bubble up to Global.asax which is where errors are getting logged in this application.
I have seen suggestions to wrap the method in a try/catch, which is a stupid way of doing it as there are a variety of errors that ASP.Net will silently swallow still.
In trying to find a solution I've seen a lot of references to SoapExtension, which is exactly what I want to do but doesn't get fired as I'm returning Json. What I really want is a way to catch the error just like that.
Any pointers appreciated, I still can't understand how the ASP.Net team could have thought that silently swallowing errors like this was a bright idea.
So for example a method like this:
[WebMethod]
[ExceptionHandling] //can I write a handler like this to catch exceptions from JSON webservices?
static public void DeleteItem(string id)
{
var api = new GoogleCalendarAPI(User.InternalUser());
api.DeleteEvent(id);
return "success";
}
There is no equivalent to SoapExtension for JSON WebMethods and having custom errors turned on in your production site will result in a generic error message being returned to the client, no error is ever raised on the server. You cannot circumvent this.
If you inspect the code using something like ILSpy, there is no way to pass a method or class to page WebMethods like SoapExtension. The error is swallowed by ASP.Net as it invokes the web method, the only notification you will get is a HTTP 500 error sent to the client with a total generic error message.
In 4.0, WebMethods get called by this:
// System.Web.Script.Services.RestHandler
internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)
{
try
{
//snip irrelevant code
RestHandler.InvokeMethod(context, methodData, rawParams);
}
catch (Exception ex)
{
RestHandler.WriteExceptionJsonString(context, ex);
}
}
So if invoking your method throws an error it will call the following code with a statusCode of 500, there's no re-throw in there and nothing else you can pass in called so unless I'm being blind it just gets swallowed silently. Even worse if you've got custom errors turned on, which any sane person will, it'll completely obfuscate the original cause:
// System.Web.Script.Services.RestHandler
internal static void WriteExceptionJsonString(HttpContext context, Exception ex, int statusCode)
{
//snip code setting up response
context.Response.TrySkipIisCustomErrors = true;
using (StreamWriter streamWriter = new StreamWriter(context.Response.OutputStream, new UTF8Encoding(false)))
{
if (ex is TargetInvocationException)
{
ex = ex.InnerException;
}
if (context.IsCustomErrorEnabled)
{
streamWriter.Write(JavaScriptSerializer.SerializeInternal(RestHandler.BuildWebServiceError(AtlasWeb.WebService_Error, string.Empty, string.Empty)));
}
else
{
streamWriter.Write(JavaScriptSerializer.SerializeInternal(RestHandler.BuildWebServiceError(ex.Message, ex.StackTrace, ex.GetType().FullName)));
}
streamWriter.Flush();
}
}
I can't see a way around it, looks like WebMethod is not ready for production code, shame.
It's not so much they get disappeared, it's more that they get passed out to the calling client. Since however you don't always want to (or should) reveal such intimate details of your service, you can prevent errors bubbling out of your service. This gives the impression of them disappearing.
Wrapping the inner detail in a try-catch is about the best way to cope with any errors. Within the method you're dealing with standard error trapping. So I think you'd want something like:
[WebMethod]
static public string DeleteItem(string id)
{
try
{
var api = new GoogleCalendarAPI(User.InternalUser());
api.DeleteEvent(id);
return "success";
}
catch(Exception ex)
{
log.fatal(ex);
return "error";
}
}
If anything throws an exception within the try-catch it'll be caught. ASP.Net won't interfere with it, unless the methods you are calling have been specifically coded to do so.
Edit
If the GoogleCalendarAPI class is in turn calling a method, such as ExecuteWebServiceCall with catches the Exception, then you'd have to parse the response. I'd hope they gave you some other clue, like a response code, to indicate an error state. You could then wrap that in an Exception, throw it have it caught by your default error handler.