I use ExceptionFilterAttribute for my web api application to catch different unhandled exceptions, i.e.:
public class InvalidDriverExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Exception != null)
{
if (actionExecutedContext.Exception is Domain.InvalidDriverException)
{
var resp = actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "User is not a driver");
actionExecutedContext.Response = resp;
}
}
//base.OnException(actionExecutedContext);
}
}
but I want to have similar engine for my web job. Is it possible?
but I want to have similar engine for my web job. Is it possible?
Yes. But because web jobs are continuous or schedule, there are some differences in how they are implemented. You could use ErrorTrigger to achieve your goal. An error trigger that allows you to annotate functions to be automatically called by the runtime when errors occur. It could monitor errors in web job when it is executed.My Demo result like this:filter null exception. For more details, you could refer to this article.
When developing jobs with Azure WebJob, it's a good practice to implement error monitoring in case something goes wrong when a job is executed.
The WebJobs ErrorTrigger extension, part of the Core extensions, will help us achieve that.
I have created derived class from FunctionExceptionFilterAttribute
public class ErrorHandlerAttribute : FunctionExceptionFilterAttribute
{
public override async Task OnExceptionAsync(FunctionExceptionContext exceptionContext, CancellationToken cancellationToken)
{
string body = $"ErrorHandler called. Function '{exceptionContext.FunctionName}': {exceptionContext.FunctionInstanceId} failed. ";
CombineErrorWithAllInnerExceptions(exceptionContext.Exception, ref body);
string[] emailList = System.Configuration.ConfigurationManager.AppSettings["SendErrorEmails"].Split(';');
await SendEmail.SendErrorNotificationAsync("WebJob - Common Driver Error", body);
}
private void CombineErrorWithAllInnerExceptions(Exception ex, ref string error)
{
error += $"ExceptionMessage: '{ex.Message}'.";
if (ex is Domain.BadStatusCodeException)
{
error += $"Status code: {((Domain.BadStatusCodeException)ex).StatusCode}";
}
if (ex.InnerException != null)
{
error += $"InnerEx: ";
CombineErrorWithAllInnerExceptions(ex.InnerException, ref error);
}
}
}
and then use it for method:
[NoAutomaticTrigger]
[ErrorHandler]
public async Task GetDriversAsync(TextWriter logger)
{
when exception occurs it call this code and send notification email to me
Related
We have void method which submits a message to azure service bus and
service bus client has send message which don't have an return value.
Please find the method below , what I can think of is make the method return a bool if there is no issue .
we are using Moq and Xunit and .Net 6 version
then I am not sure how to mock the _azureClientFactory.
is it a normal practice to skip this type of method from unit testing
can anybody help me on this or please share any pointers
public async Task SendMessage(string message)
{
try
{
var serviceBusCLient = _azureClientFactory.CreateClient("SubmitClient");
var serviceBusSender = serviceBusCLient.CreateSender("queue1");
var serviceBusMessage = new ServiceBusMessage(message);
await serviceBusSender.SendMessageAsync(serviceBusMessage);
}
catch (Exception ex)
{
_logger.LogError("ex.Message}");
throw new AzureBusServiceException($"sendmessage failed");
}
}
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;
}
In my Web API project, I created sub projects (class libraries) where I handle actual data handling operations. My backend database is DocumentDB.
My question is how do I tell my Web API action methods of any errors I may encounter within data methods in my class libraries? Once my Web API method knows about the error, I can just return Http status 500 or something like that but I'm not sure what I should have in the catch part (see below) and how I can notify the calling Web API method of the error encountered?
--- Web API Method ---
public async Task<IHttpActionResult> DoSomething(Employee emp)
{
var employeeRecord = await MyClassLibrary.DoSomethingWithEmployee(emp);
// Here, I want to check for errors
}
--- Class Library Code ---
public static async Task<Employee> DoSomethingWithEmployee(Employee emp)
{
try
{
// Logic here to call DocumentDB and create employee document
}
catch
{
// This is where I catch the error but how do I notify the calling Web API method that there was an error?
}
}
ASP.NET Web API 2.1 have framework support for global handling of unhandled exceptions.
It allows use to customize the HTTP response that is sent when an unhandled application exception occurs.
So, do not catch exception in Class Library. If you are required to log exception in Class Library, then re-throw those exception to Presentation.
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ...
config.Services.Replace(typeof (IExceptionHandler),
new GlobalExceptionHandler());
}
}
GlobalExceptionHandler
public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var exception = context.Exception;
var httpException = exception as HttpException;
if (httpException != null)
{
context.Result = new CustomErrorResult(context.Request,
(HttpStatusCode) httpException.GetHttpCode(),
httpException.Message);
return;
}
// Return HttpStatusCode for other types of exception.
context.Result = new CustomErrorResult(context.Request,
HttpStatusCode.InternalServerError,
exception.Message);
}
}
CustomErrorResult
public class CustomErrorResult : IHttpActionResult
{
private readonly string _errorMessage;
private readonly HttpRequestMessage _requestMessage;
private readonly HttpStatusCode _statusCode;
public CustomErrorResult(HttpRequestMessage requestMessage,
HttpStatusCode statusCode, string errorMessage)
{
_requestMessage = requestMessage;
_statusCode = statusCode;
_errorMessage = errorMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
return Task.FromResult(_requestMessage.CreateErrorResponse(
_statusCode, _errorMessage));
}
}
Credit to ASP.NET Web API 2: Building a REST Service from Start to Finish, and source code.
The error handling depends on your logic and how your API respond to its consumers.
Basically, you have to use HTTP Status Codes according to the type of error.
In your data access and business layer methods, you can depend on the return type. For example, in all methods that queries the database, if the object is not there, you can return NULL, and in your web API, if the method returns NULL, then simply return NotFound() which will respond to the client with a 404.
As for the exceptions:
You can use Error Codes in your business and data access layer and check for these codes in your web API actions. Then return a suitable status code accordingly. Ex: return a status code of 500 if there has been a connection issue to the database, or return a 400 (Bad Request) if the user didn't provide all required action parameters in the correct format.
In case of any other exception that you didn't catch, you can go with the global exception handler described by #Win
If you want to intercept and log the error in your console application but still forward the error to the caller, just use throw; at the end of your catch statement in your console application.
It will rethrow the same exception to the caller, so your application can be aware of the exception in the "callee" as well as in the "caller".
Lately I am working on exception logging module of a WCF service. Unfortunately the service hasn't been introduced with unit tests, therefore there are many unexpected exceptions occurring. And so far I have accomplished to get the exceptions with interceptor aproach, by implementing IErrorHandler interface and tying it to the service interface with IServiceBehaviour. I liked this functionality very much actually. But it brought me into a next step of desire of getting the details of exception. Like on which line did the exception occurred?
I can satisfy this desire by 2 ways in my mind:
By having a variable for keeping track of the lines I've passed through successfully, and including it in the exception thrown.
By catching exceptions from all lines seperately.
But both approaches seem very lousy to me. I am wondering is there a known design pattern or a tool to achive this goal?
In my opinion you might try using logging, such as log4net. Then you can find out where is and what happened. Exception object not always contains the stack info, because of "inlining", that occur during optimization etc.
include the PDB files for your service and the line numbers will be included in exception.ToString()
The way we have solved this problem is twofold:
Our services are dumb wrappers around commands. So when a service method is entered it delegates its work to a command.
We wrap every command call in a logging proxy that is responsible for logging input, output and errors and executing the command.
For example:
public FooServiceModel GetFoo(int fooId)
{
return new ILogged<GetFooCommand>().Target.Execute(fooId);
}
This delegates execution of the command to ILogged which:
Logs the command name
Logs the command parameters
Logs the execution result
Logs any exceptions
It also does some other stuff to link up the client request with the server call using custom message headers so that a call can be completely debugged from client to server and back. This is incredibly useful and allows us to diagnose even complex problems off site.
We use the Castle.Core dynamic proxy to implement ILogged with an interceptor that looks something like this (ILog is a log4net logger):
public class LoggingInterceptor : IInterceptor
{
public LoggingInterceptor([NotNull] object target, [NotNull] ILog logger)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (logger == null)
{
throw new ArgumentNullException("logger");
}
this.Target = target;
this.Logger = logger;
}
public object Target { get; set; }
public ILog Logger { get; set; }
public void Intercept(IInvocation invocation)
{
try
{
this.Logger.Debug(invocation);
invocation.ReturnValue = invocation.Method.Invoke(
this.Target, invocation.Arguments);
this.Logger.Debug("Invocation return value:");
this.Logger.Debug(invocation.ReturnValue);
}
catch (TargetInvocationException ex)
{
this.Logger.Error("Unable to execute invocation", ex);
if (ex.InnerException != null)
{
throw ex.InnerException;
}
throw;
}
}
}
The invocation itself is rendered by a custom log4net object renderer:
public class InvocationRenderer : IObjectRenderer
{
public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
{
var invocation = (IInvocation)obj;
var builder = new StringBuilder();
builder.AppendFormat(
"Invoking Method: {0} --> '{1}' with parameters (",
invocation.Method.DeclaringType != null
? invocation.Method.DeclaringType.FullName : "{Unknown Type}",
invocation.Method);
var parameters = invocation.Method
.GetParameters()
.Zip(invocation.Arguments, (p, a) => new { Parameter = p, Argument = a })
.ToArray();
var index = 0;
foreach (var parameter in parameters)
{
builder.AppendFormat(
"{0}: {1}",
parameter.Parameter.Name,
rendererMap.FindAndRender(parameter.Argument));
if (++index < parameters.Length)
{
builder.Append(", ");
}
}
builder.Append(")");
writer.Write(builder.ToString());
}
}
Hopefully that will give you some ideas on how to tackle this problem.
I use the SignalR 1.0.
When exception occurs on the server, the client gets a message like this
{"I":"0","E":"Exception of type 'System.Exception' was thrown.","T":" at METHODNAME in d:\PATH\TO\HUB.cs:line 227\r\n at METHODNAME in d:\PATH\TO\HUB.cs:line 51"}
But I want to make it more user-friendly. How to I can do it?
I have read a suggestion to put all server methods into try-catch block. But I think that it is not a true-way.
I traced the Exception and found that the Exception was catched in the Microsoft.AspNet.SignalR.Hubs.HubDispatcher.Incoming method. But it is internal static method which I cannot customize.
In the ideal case I want to get ability to convert an exception to a valid response.
You can use a HubPipelineModule.
For example:
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hubs;
public class MyHubPipelineModule : HubPipelineModule
{
protected override Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
{
return async context =>
{
try
{
// This is responsible for invoking every server-side Hub method in your SignalR app.
return await invoke(context);
}
catch (Exception e)
{
// If a Hub method throws, have it return the error message instead.
return e.Message;
}
};
}
}
Then in your Global.asax.cs:
protected void Application_Start()
{
GlobalHost.HubPipeline.AddModule(new MyHubPipelineModule());
//...
RouteTable.Routes.MapHubs();
}