Capture exception during request deserialization in WebAPI C# - c#

I'm using WebAPI v2.2 and I am getting WebAPI to deserialise JSON onto an object using [FromBody] attribute. The target class of the deserialisation has a [OnDeserialized] attribute on an internal method, like this:
[OnDeserialized]
internal void OnDeserialisedMethod(StreamingContext context) {
// my method code
}
I know for a fact there is a problem with the code inside this method, I've stepped through it and found it. The problem for me is that I get no exception at all. What happens is this method gets jumped out of and the exception seems to be ignored. My controller action gets called and my target object is not properly populated because this serialisation method has not been correctly executed.
My question is; how can I capture an exception that occurs during deserialisation in WebAPI?

I've written up a filter (as suggested in various comments) that checks the ModelState and throws an exception if serialization errors did occur. Beware though, that this may not contain only serialization exceptions - that could be adjusted by specifing the concrete exception type in the Select statement.
public class ValidModelsOnlyFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid)
{
base.OnActionExecuting(actionContext);
}
else
{
var exceptions = new List<Exception>();
foreach (var state in actionContext.ModelState)
{
if (state.Value.Errors.Count != 0)
{
exceptions.AddRange(state.Value.Errors.Select(error => error.Exception));
}
}
if (exceptions.Count > 0)
throw new AggregateException(exceptions);
}
}
}
I suggest binding this filter on a global scope. I really can't fathom why it should be ok to ignore deserialization exceptions.

I had exactly the same problem and bookmarked your question in hope that someone would provide a solution. I thought using ModelState implied rewriting some validations in the JSON model, but it just works, in fact it's simple and very well done. I didn't have to modify the model, just the controllers.
My code from one of my controllers, StdResponse being the class used to provide the response with details if needed (in this case, for instance) :
[HttpPost]
public StdResponse Test([FromBody]StdRequest request)
{
if (ModelState.IsValid)
{
//Work on the data from the request...
}
else
{
//Retrieve the exceptions raised during deserialization
var errors = ModelState.SelectMany(v => v.Value.Errors.Select(e => e.Exception));
List<String> messages = new List<string>();
foreach (Exception e in errors)
{
messages.Add(e.GetType().ToString() + ": " + e.Message);
}
return new StdResponse(exchangeVersion, "null", ExecutionResponse.WithError("StdRequest invalid", messages));
}
}

You can check ModelState.IsValid inside your controller.
If "OnDeserialisedMethod" throws an exception (or any other model validation fails) it will be false, if everything succeed it will be true.

Related

Returning meaningful error messages from a .NET Core API

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;
}

Returning BadRequest from WebApi Custom method

Using .net core web api here.
I have a endpoint in my api as:
[HttpPost("data")]
public async Task<IActionResult> PostData(List<string> udata)
{
JArray sets = new JArray();
try
{
sets = Helper.GetData(udata);
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
In the above I am calling an custom method in my helper "GetData" which does all the processing and calculations. To make the Api controller clean I moved all the processing to this helper method.
Below is my helper class:
public static class Helper
{
public static BadRequestObjectResult GetMessage(string message)
{
return new BadRequestObjectResult(message);
}
public static JArray GetData(List<string> udata)
{
if(udata == null)
return GetMessage("Data is null");
//do some processing and calclulations here
//return BadRequest if some issue
}
}
If there is some processing issue or some of the data is not as intended or some other issue I want to throw BadRequest. For this I made a custom method to do so "BadRequestObjectResult".
Now my issue is, if there is an issue in GetData it doesnt throws back to my api or exits from my loop. It just continues to next statement.
I know there is some issue by the way I am returning this but not able to find the issue.
Can anyone point out the correct way to handle this?
My suggestion is to throw an exception from your Helper class and and handle it from your PostData method. For example...
You could throw an ArgumentException and explicitly catch that from your API method.
public static class Helper
{
public static JArray GetData(List<string> udata)
{
if(udata == null)
throw new ArgumentException("Data is null");
//do some processing and calclulations here
//throw ArgumentException if there is an issue
}
}
[HttpPost("data")]
public async Task<IActionResult> PostData(List<string> udata)
{
JArray sets = new JArray();
try
{
sets = Helper.GetData(udata);
return Ok(sets);
}
catch (ArgumentException e)
{
return BadRequest(e.Message);
}
}
This way you can only worry about return codes from your controller while your Helper methods are only concerned with the input and aren't returning something specialized for a controller. It's a bit more flexible that way if you ever want to use your Helper class elsewhere.
This will also meet your requirement of stopping processing upon hitting a bad result, so as soon as a bad result is encountered the result set is thrown away and a BadRequest response is issued.

Web API 2 services - how to return an Exception message in the Status

Is it possible, in Web API 2 to directly return the Exception message in the response's Status ?
For example, if I was writing a WCF Service (rather than Webi API), I could follow this tutorial to directly return an Exception message as part of the response status:
Here, the web service doesn't return any data in the Response, and the error message gets returned directly in the Status Description.
This is exactly what I'd like my Web API services to do when an exception occurs, but I can't work out how to do it.
Most suggestions suggest using code like below, but then the error message will then always get returned in a separate response string, rather than being part of the Status.
For example, if I were to use this code:
public IHttpActionResult GetAllProducts()
{
try
{
// Let's get our service to throw an Exception
throw new Exception("Something went wrong");
return Ok(products);
}
catch (Exception ex)
{
return new System.Web.Http.Results.ResponseMessageResult(
Request.CreateErrorResponse((HttpStatusCode)500,
new HttpError("Something went wrong")));
}
}
... then it returns a generic 500 message, and the exception is returned in a JSON string.
Does anyone know how to modify a Web API function (which returns an IHttpActionResult object) to do this ?
You could register a custom global filter that will handle all Exceptions. Something like:
public class CatchAllExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message)
};
}
}
You will need to register it in WebApiConfig.cs, with:
config.Filters.Add(new CatchAllExceptionFilterAttribute());
This filter will be hit everytime there is an unhandled exception in the system and set the http response to the exception message. You could also check the different types of exception and alter your response accordingly, for example:
public override void OnException(HttpActionExecutedContext context)
{
if(context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent("Method not implemented.")
};
}
else
{
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message)
};
}
}
https://www.asp.net/web-api/overview/error-handling/web-api-global-error-handling
Please refer above link, it will help you!

Detailed exception logging on wcf

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.

Handling error from binding action parameters to route values of incorrect types

I'm having troubles handling all types of errors in ASP.NET WebAPI.
I've successfully handled exceptions thrown inside my action methods using an ExceptionFilter and 404 errors for invalid routes, invalid controller or action name. However, I'm struggling to handle the error where the controller and action are both found, but the parameters for model binding are incorrect types.
Here's my action, which is routed to /api/users/{id}.
[HttpGet]
public virtual TPoco Get(long id)
{
...
}
If I request the URL /api/users/notinteger, I get a 400 Bad Request error that is handled outside of my code:
{
Message: "The request is invalid.",
MessageDetail: "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int64' for method '___ Get(Int64)' in '___'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
How can I intercept this error and respond with my own error message? Preferably not in the controller itself as I'd like to handle several controllers in the same way.
I've tried using global.asax.cs's Application_Error event as per this question, but that doesn't seem to be called in this case.
It appears that these errors are added to the ModelState as model binding errors. The action selector selects the correct action and the action invoker invokes it without throwing any errors.
The workaround I came up with is to create an action invoker that checks the ModelState for errors. If it finds any, it passes the first one to the exception handling method used by my ExceptionFilter and ErrorController.
internal class ThrowModelStateErrorsActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
foreach (var error in actionContext.ModelState.SelectMany(kvp => kvp.Value.Errors))
{
var exception = error.Exception ?? new ArgumentException(error.ErrorMessage);
//invoke global exception handling
}
return base.InvokeActionAsync(actionContext, cancellationToken);
}
}
It's nasty, but it works. This has taken up most of my day and I'm just glad to have finally got somewhere.
I'd be interested to know what the consequences are to this. What else uses the ModelState errors in Web API? Could anyone add some possible flaws in this solution?
It will more better if you use the new WebApi 2.1 Global error handling as discussed here,
http://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling&referringTitle=Specs
http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error
If you are not willing to use WebApi 2.1 for a valid reason then you can try. (Note I have not tested but you can try). Create a custom action descriptor by inheriting with ReflectedHttpActionDescriptor and handle ExecuteAsync. This is what I mean,
public class HttpNotFoundActionDescriptor : ReflectedHttpActionDescriptor
{
ReflectedHttpActionDescriptor _descriptor;
public HttpNotFoundActionDescriptor(ReflectedHttpActionDescriptor descriptor)
{
_descriptor = descriptor;
}
public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken)
{
try
{
return descriptor.ExecuteAsync(controllerContext, arguments, cancellationToken);
}
catch (HttpResponseException ex)
{
//..........................
}
}
}
public class HttpNotFoundAwareControllerActionSelector : ApiControllerActionSelector
{
public HttpNotFoundAwareControllerActionSelector()
{
}
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
HttpActionDescriptor decriptor = null;
try
{
decriptor = base.SelectAction(controllerContext);
}
catch (HttpResponseException ex)
{
var code = ex.Response.StatusCode;
if (code != HttpStatusCode.NotFound && code != HttpStatusCode.MethodNotAllowed)
throw;
var routeData = controllerContext.RouteData;
routeData.Values["action"] = "Handle404";
IHttpController httpController = new ErrorController();
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "Error", httpController.GetType());
decriptor = base.SelectAction(controllerContext);
}
return new HttpNotFoundActionDescriptor(decriptor);
}
}
Note you need to override all the virtual methods.

Categories