I have a .NET appplication where there is a controller for receiving user requests, a service Service 1 which calls another service Service 2.
I have some code in the Service 2 where I query the database(DynamoDB) and get a 500 error in response when the user request values are incorrect. I want to handle this such that I catch this error/exception and send back the error message along with a 400 status code from the controller to the user. How should I modify the code to do this?
This is what I have tried. Currently, I'm just printing the error in Service 1 but I need to send it to the controller. Is sending the error message to the controller by throwing exceptions along the way the right way to do it?
The below code is similar to the actual code
Controller:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Service 1:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
Console.WriteLine(e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
Service 2:
private async Task<DynamoResult<TResponse>> GetPagedQueryResults(DynamoQuery query)
{
var results = new List<Document>();
try{
results = await search.GetNextSetAsync();
}
catch(Exception e){
throw new PaginationTokenException(e.Message);
}
return results;
}
[Serializable]
public class PaginationTokenException : Exception
{
public PaginationTokenException() { }
public PaginationTokenException(string message)
: base(message) {
throw new Exception(message);
}
public PaginationTokenException(string message, Exception inner)
: base(message, inner) { }
}
Assuming you want to hide implementation details from the controller (i.e. you don't want the controller to know/care that it's DynamoDB), I would create a custom exception and throw that from Service1.
Service1 would look something like this:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
throw new MyCustomException('My error message', e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
In the controller you can then capture that exception explicitly:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (MyCustomException e)
{
return BadRequest(e.Message);
}
}
Related
My Api Service is in .NET and my client side is in React.js. I use axios.post to send parameters and retrieve datas from .NET. I want to see error details on react.js side when something happened in service side. Example codes are below;
[HttpPost]
public ConcreteAccrument CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
ConcreteApplication application = depositAmountParameters.application;
int multiplier = depositAmountParameters.multiplier;
bool forceCalculation = depositAmountParameters.forceCalculation;
long registryInfoOid = depositAmountParameters.registryInfoOid;
long subscriberRegistryOid = depositAmountParameters.subscriberRegistryOid;
try
{
Com.BS.WaterSupplyAndSeverage.Services.WaterSupplyAndSewerage wssService = new Com.BS.WaterSupplyAndSeverage.Services.WaterSupplyAndSewerage();
return wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
}
catch (BSException e)
{
FileLogger.Error(CLASS_NAME, "CalculateDepositAmount", e.Message, e.StackTrace, application, multiplier, forceCalculation);
BSCommunicationException commException = new BSCommunicationException();
commException.Id = e.Id;
commException.ExceptionMessage = e.ExceptionMessage;
throw new FaultException<BSCommunicationException>(commException, new FaultReason(commException.ExceptionMessage));
}
catch (Exception e)
{
FileLogger.Error(CLASS_NAME, "CalculateDepositAmount", e.Message, e.StackTrace, application, multiplier, forceCalculation);
BSCommunicationException commException = PrepareCommunicationException(e);
throw new FaultException<BSCommunicationException>(commException, new FaultReason(commException.ExceptionMessage));
}
}
There are some details in throw new FaultException at first catch(BSException e). It's not a system error. For example, data is null or some value are missing when first catch works. And second catch is system error. But in that code all catches return 500 error in React.Js side. All I want is to see all detail in first catch on React.js side. When I use "return error" in catch then I get convert error because my class return an object.
Here my react.js code;
export const CalculateDepositAmount = (APPLICATION,MULTIPLIER,FORCE_CALCULATION,REGISTRY_INFO_OID, SUBSCRIBER_REGISTRY_OID, SuccessOperation, FailedOperation) => {
return () => {
const body = { application:APPLICATION,multiplier:MULTIPLIER,forceCalculation:FORCE_CALCULATION,registryInfoOid:REGISTRY_INFO_OID, subscriberRegistryOid:SUBSCRIBER_REGISTRY_OID};
console.log("bodyFormData",body)
axios.post('https://localhost:44396/api/CalculateDepositAmount', body)
.then( async response => {
SuccessOperation({ CALCULATED_DEPOSIT_AMOUNT_DATA: await response.data });
})
.catch(() => {
FailedOperation({ CALCULATED_DEPOSIT_AMOUNT_DATA: null })
});
}
}
I am assuming that this is not asp.net core / 5 / 6, but vanilla 4.x
One thing you can do is change the method signature to IHttpActionResult, so you can return different status codes, with varying payloads back to the client:
public IHttpActionResult CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
try
{
var result = wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
return Ok(result);
}
catch (BSException e)
{
return BadRequest(e.Message)
//or
//return StatusCode(418)
}
catch (Exception e)
{
}
}
You can tailor the response to the client much better to your needs, instead of return either the object or an exception. You can find the full list of here:
https://learn.microsoft.com/en-us/previous-versions/aspnet/dn314678(v=vs.118)?redirectedfrom=MSDN
Another approach that will require some more refactoring, is to change the return type of your service to some sort of Result object, that indicates, whether it is a successfull operation or if a problem occured.
For example take this CommandResult example:
public class CommandResult<T>
{
private CommandResult(T payload) => Payload = payload;
private CommandResult(string failureReason)
{
FailureReason = failureReason;
}
public string FailureReason { get; }
public string Message { get; }
public bool IsSuccess => string.IsNullOrEmpty(FailureReason);
public T Payload { get; }
public static implicit operator bool(CommandResult<T> result) => result.IsSuccess;
public static CommandResult<T> Success(T payload)
=> new(payload);
public static CommandResult<T> Fail(string reason)
=> new(reason);
}
In your service you can now do the following:
public Commandresult<ConcreteAccrument> CalculateDepositAmount(DepositAmountParameters depositAmountParameters)
{
try
{
var result = // do the calculation
return CommandResult<ConcreteAccrument>.Success(result);
}
catch (BSException e)
{
return CommandResult<ConcreteAccrument>.Fail(e.Message);
}
catch (Exception e)
{
return CommandResult<ConcreteAccrument>.Fail(e.Message);
}
}
Now your controller simply has to decide, if it was successfull or not:
public IHttpActionResult CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
var result = wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
if(result.IsSuccess) // or simply if (result)
{
return Ok(result.Payload);
}
return Exception(result.FailureReason); //or whatever suits best.
}
I'm trying to suppress error in code, but MVC action still returns "500 internal server error".
What events are fired in ASP.NET Boilerplate framework after action returns?
public async Task<IActionResult> Post([FromBody]PaymentViewModel model)
{
var result = false;
// Storing of card must pass
try
{
// ...
}
catch (Exception ex)
{
// Catch business exception, but storing
}
return Json(new { result });
}
To recover in a catch block, begin a UnitOfWork with RequiresNew:
public async Task<IActionResult> Post([FromBody]PaymentViewModel model)
{
var result = false;
// Storing of card must pass
try
{
using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
{
// ...
await CurrentUnitOfWork.SaveChangesAsync();
await uow.CompleteAsync();
}
}
catch (Exception ex)
{
// Catch business exception, but storing
}
return Json(new { result });
}
Further explanation: aspnetboilerplate/aspnetboilerplate#2732 (comment)
I am trying to understand custom exceptionhandlers but am not getting the hang of it. I tried implementing a custom exception handler like explained in the following pages:
https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/web-api-global-error-handling
Now my code is:
public class CustomRestErrorHandlerException : ExceptionHandler
{
public void CustomError(String error)
{
var message = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent("An unknown error occurred saying" + error)
};
throw new HttpResponseException(message);
}
public override void Handle(ExceptionHandlerContext context)
{
if (context.Exception is ArgumentNullException)
{
var result = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "ArgumentNullException"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentException)
{
var result = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is not found"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentOutOfRangeException)
{
var result = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is out of range"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else
{
CustomError(context.Exception.Message);
}
}
public class ErrorMessageResult : IHttpActionResult
{
private HttpRequestMessage _request;
private HttpResponseMessage _httpResponseMessage;
public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
{
_request = request;
_httpResponseMessage = httpResponseMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_httpResponseMessage);
}
}
}
Then I try to call the exceptionhandler which I obviously do wrong : (this I probably do not understand <<
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new CustomRestErrorHandlerException(); << error occurs here
}
return this.Ok();
}
As you can see the error occurs because the exception is not an exceptionhandler but I have no idea how to then throw an exception through an custom exception handler since it's explained nowhere.
Can anyone explain this to me with a small example perhaps?
ExceptionHandler's have to be registered in the Web API configuration. This can be done in the WebApiConfig.cs file as shown below, where config is of type System.Web.Http.HttpConfiguration.
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
Once they are registered they are automatically called during unhandled exceptions. To test it out you might want to throw an exception in the action method such as:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new ArgumentNullException(); << error occurs here
}
return this.Ok();
}
You can now put a breakpoint in your exception handler and see how the unhandled exception is caught by the global ExceptionHandler.
Quote from: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/:
"The handler, like the logger, must be registered in the Web API configuration. Note that we can only have one Exception Handler per application."
config.Services.Replace(typeof(IExceptionHandler), new CustomRestErrorHandlerException ());
So add the line above to the WebApiConfig.cs file and then simply throw an exception from the controller:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
// do not use try catch here
//try
//{
// try clearing
//}
//catch(Exception e)
//{
//throw new CustomRestErrorHandlerException(); << error occurs here
//}
throw new Exception();
}
In Framework WebAPI 2, I have a controller that looks like this:
[Route("create-license/{licenseKey}")]
public async Task<LicenseDetails> CreateLicenseAsync(string licenseKey, CreateLicenseRequest license)
{
try
{
// ... controller-y stuff
return await _service.DoSomethingAsync(license).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.Error(e);
const string msg = "Unable to PUT license creation request";
throw new HttpResponseException(HttpStatusCode.InternalServerError, msg);
}
}
Sure enough, I get back a 500 error with the message.
How can I do something similar in ASP.NET Core Web API?
HttpRequestException doesn't seem to exist. I would prefer to continue returning the object instead of HttpRequestMessage.
What about something like this. Create a middleware where you will expose certain exception messages:
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
context.Response.ContentType = "text/plain";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
if (ex is ApplicationException)
{
await context.Response.WriteAsync(ex.Message);
}
}
}
}
Use it in your app:
app.UseMiddleware<ExceptionMiddleware>();
app.UseMvc();
And then in your action throw the exception:
[Route("create-license/{licenseKey}")]
public async Task<LicenseDetails> CreateLicenseAsync(string licenseKey, CreateLicenseRequest license)
{
try
{
// ... controller-y stuff
return await _service.DoSomethingAsync(license).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.Error(e);
const string msg = "Unable to PUT license creation request";
throw new ApplicationException(msg);
}
}
A better approach is to return an IActionResult. That way you dont have to throw an exception around. Like this:
[Route("create-license/{licenseKey}")]
public async Task<IActionResult> CreateLicenseAsync(string licenseKey, CreateLicenseRequest license)
{
try
{
// ... controller-y stuff
return Ok(await _service.DoSomethingAsync(license).ConfigureAwait(false));
}
catch (Exception e)
{
_logger.Error(e);
const string msg = "Unable to PUT license creation request";
return StatusCode((int)HttpStatusCode.InternalServerError, msg)
}
}
It's better not to catch all exceptions in every action. Just catch exceptions you need to react specifically and catch (and wrap to HttpResponse) all the rest in Middleware.
I have a series of methods that call wcf services and all of them have the same try catch code
Response Method1(Request request)
{
Response response = null;
using(ChannelFactory<IService1> factory = new ChannelFactory<IService1>(myEndpoint))
{
IService1 channel = factory.CreateChannel();
try
{
response = channel.Operation(request);
}
catch(CommunicationException ex)
{
// Handle Exception
}
catch(TimeoutException ex)
{
// Handle Exception
}
catch(Exception ex)
{
// Handle Exception
}
}
return response;
}
And so on (I have 6 methods like this for different services).. how can i encapsulate all the service calls and handle the exceptions in a single method
EDIT
Following Nathan A's advice I created a simple generic method:
protected TResult ExecuteAndCatch<TResult>(Func<T, TResult> serviceCall, T request)
where T : Request
where TResult : Response
{
try
{
return serviceCall(request);
}
catch (CommunicationException ex)
{
}
catch (TimeoutException ex)
{
}
catch (Exception ex)
{
}
return null;
}
The new methods would like this
Response NewMethod1(Request request)
{
Response response = null;
using(ChannelFactory<IService1> factory = new ChannelFactory<IService1>(myEndpoint))
{
IService1 channel = factory.CreateChannel();
response = channel.Operation(request);
}
return response;
}
and i'm trying to call it like
Response response = ExecuteAndCatch<Response>(NewMethod1, new Request())
What am I doing wrong?
Use a wrapper function.
Take a look at this article: http://mytenpennies.wikidot.com/blog:writing-wcf-wrapper-and-catching-common-exceptions
Here's an example from the article:
private void ExecuteAndCatch<T> (Action<T> action, T t) {
try {
action (t);
Success = true;
}
catch (TimeoutException) {
Success = false;
Message = "Timeout exception raised.";
}
catch (CommunicationException) {
Success = false;
Message = "Communication exception raised.";
}
}
If your client derives from ClientBase<T> e.g MyClient : ClientBase<IWCFService>
You could then create your own base class that provides methods that will wrap the common functionality.
The below sample code could be expanded to allow the final derived class to specify what to do when a particular method call fails. Here I just call HandleError
In specific client class
//method that returns a value
public int Ping()
{
return Protect(c => c.Ping());
}
//void method usage
public void Nothing(int stuff)
{
Protect(c => c.Nothing(stuff));
}
In client base class
protected void Protect(Action<IWCFService> action)
{
Protect(c => { action(c); return true; });
}
//add other exception handling
protected Protect<T>(Func<IWCFService, T> func)
{
try
{
return func(Channel);
}
catch (FaultException e)
{
HandleError(e);//up to you to implement this and any others
}
return default(T);
}
inject the various clients through an interface and then run the operation in a single place?
HttpResponse performOperation(IServiceClient injectedServiceClient)
{
IServiceClient client = injectedServiceClient;
try
{
client.Operation();
}
catch(CommunicationException ex)
{
// Handle Exception
}
catch(TimeoutException ex)
{
// Handle Exception
}
catch(Exception ex)
{
// Handle Exception
}
return httpResponse(httpStatusCode.OK);
}