In my project I'm calling a lot of WebApi with Refit (link). Basically, I define the WebApi as an interface. For example:
public interface ICustomer
{
[Get("/v1/customer")]
Task<CustomerResponse> GetDetails([Header("ApiKey")] string apikey,
[Header("Authorization")] string token,
[Header("Referer")] string referer);
}
For each WebApi, I create a client like that:
public async Task<CustomerResponse> GetDetails(string apikey, string token)
{
CustomerResponse rsl = new CustomerResponse();
rsl.Success = false;
var customer = RestService.For<ICustomer>(apiUrl);
try
{
rsl = await customer.GetDetails(apikey, token, apiUrl);
rsl.Success = true;
}
catch (ApiException ax)
{
rsl.ErrorMessage = ax.Message;
}
catch (Exception ex)
{
rsl.ErrorMessage = ex.Message;
}
return rsl;
}
The only difference between clients are the interface (in the above example code ICustomer), the return structure (in the example CustomerResponse derives from BaseResponse), and the function I have to call (in the example GetDetails with params).
I should have a base class to avoid duplicated code.
Thanks in advance.
I like when people gives you a negative feedback without any explanation or a solution. If someone has a similar problem of mine, it can find my generic class to resolve this problem.
public class BaseClient<T> where T : IGeneric
{
public const string apiUrl = "<yoururl>";
public T client;
public BaseClient() : base() {
client = RestService.For<T>(apiUrl);
}
public async Task<TResult> ExecFuncAsync<TResult>(Func<TResult> func)
where TResult : BaseResponse
{
TResult rsl = default(TResult);
T apikey = RestService.For<T>(apiUrl);
try
{
rsl = func.Invoke();
rsl.Success = true;
}
catch (ApiException ax)
{
rsl.ErrorMessage = ax.Message;
}
catch (Exception ex)
{
rsl.ErrorMessage = ex.Message;
}
return rsl;
}
public async Task<List<TResult>> ExecFuncListAsync<TResult>(Func<List<TResult>> func)
{
List<TResult> rsl = default(List<TResult>);
T apikey = RestService.For<T>(apiUrl);
try
{
rsl = func.Invoke();
}
catch (ApiException ax)
{
}
catch (Exception ex)
{
}
return rsl;
}
}
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 am trying to convert my TryCatch exception handling used in multiple places to a generic method.
Aim here is for the custom method to take service method as parameter and return the result as Customer object.
I am using async Task so have written a basic ErrorHandling method as such
public Task<T> ErrorHandlingWrapper<T>(Func<Task<T>> action)
{
try
{
return action();
//return Ok(action());
}
catch (Exception ex)
{
throw new Exception(ex.Message);
//return StatusCode(500, e.Message);
}
}
In the above method I want to replicate what my original async Task does as shown below:
[HttpPost("{custId}")]
[Authorize]
public async Task<IActionResult> GetCustomer(int custId)
{
Models.Customer customer;
try
{
customer = await _service.GetCustomer(custId);
if(customer == null)
{
return BadRequest("Error retrieving customer");
}
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
return Ok(note);
}
After modifying my original method to use the new error handling method
[HttpPost("{custId}")]
[Authorize]
public async Task<IActionResult> GetCustomer(int custId)
{
return ErrorHandlingWrapper<Models.Customer>(async () =>
await _service.GetCustomer(custId)
);
}
With the above code am getting an exception
Cannot implicitly convert type System.Threading.Tasks.Task To return type IActionResult?
I am not really sure if my approach is correct?
Update
Customer.cs
public class Customer{
public int CustId { get; set; }
public string FirtName { get; set; }
public string LastName { get; set; }
}
The problem is with some wrong return types and the missing use of async/await. I refactored your example to return IActionResult. This should do:
public async Task<IActionResult> ErrorHandlingWrapper<T>(Func<Task<T>> action)
{
try
{
//return action();
return Ok(await action());
}
catch (Exception ex)
{
//throw new Exception(ex.Message);
return StatusCode(500, ex.Message);
}
}
[HttpPost("{custId}")]
[Authorize]
public async Task<IActionResult> GetCustomer(int custId)
{
return await ErrorHandlingWrapper(async () =>
await _service.GetCustomer(custId)
);
}
public async Task<ActionResult> Index()
{
var service = new CoreServiceFactory().GetImpersonatingService();
try
{
var data = new Impersonation()
{
ImpersonatingId = "dac733c3-01ad-447b-b0df-3a7c21fef90b",
UserId = "dac733c3-01ad-447b-b0df-3a7c21fef90b"
};
var imp = await service.Add(data);
}catch(Exception ex) { throw ex; }
return View();
}
Above is one of my controllers action method. And this works fine when the insertion is successful. This should fail if the data already exists in database(unique constraints). So when i intentionally try to make it fail(i manually add the same record in the db and then try to add it again via this action method) the action method goes into a loop or something, the exception is never thrown , chrome keeps me showing me the loading icon , looks like it went into some deadlock state. Can someone please help me understand why it goes into deadlock state when exception is thrown and how can i handle it?
Below are the reference methods
service.Add(data)
public async Task<Impersonation> Add(Impersonation t)
{
if (ValidateData(t))
{
using (var uow = GetUnitOfWork())
{
var r = GetRepository(uow);
var item = r.Add(t);
try
{
var ret = await uow.Save();
if (ret > 0)
{
return item;
}
else
{
return null;
}
}
catch (Exception ex)
{
throw ex;
}
}
}
else
{
throw new ValidationException(null, "error");
}
}
uow.Save()
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
throw ex;
}
}
}
Here is my suggestion: in uow.Save, log the error in the catch block and return zero (do not throw any exceptions).
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
// log the error here
return 0;
}
}
}
I'm not sure if returning the null in the Add service is a good idea or not, you might need to handle that differently too.
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);
}
I've implemented a command pattern in a project I'm working on. This is pretty much the current structure:
public class Response
{
public bool Success { get; private set; }
public static Response CreateErrorResponse()
{
return new Response { Success = false };
}
}
public interface ICommand<T> where T : Response
{
Task<T> ExecuteAsync();
}
public abstract CommandBase : ICommand<T> where T: Response
{
protected abstract Uri BuildUrl();
protected abstract Task<T> HandleResponseAsync();
public async override Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(url);
return await HandleResponseAsync(response);
}
}
I want to handle any exceptions that could be thrown by the HttpClient, so I want to change CommandBase.ExecuteAsync to something like this...
public async override Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new HttpClient();
try
{
var response = await httpClient.GetAsync(url);
return await HandleResponseAsync(response);
}
catch (HttpRequestException hex)
{
return Response.CreateErrorResponse(); // doesn't compile
}
}
The compile error I get is "Cannot convert type Response to async return type T". I can't use T.CreateErrorResponse(), as outlined in this question.
How can I work around this?
Edit to downvoters: whether or not you agree with catching exceptions in a library like this, the question still stands!
Although I am not sure this is the best solution (or feasible in your specific use case), what you can do is:
public class Response
{
public bool Success { get; private set; }
public ExceptionDispatchInfo ErrorInfo { get; private set; }
public bool HasFailed
{
get { return !Success; }
}
public static T CreateErrorResponse<T>(ExceptionDispatchInfo errorInfo) where T : Response, new()
{
var response = new T();
response.Success = false;
response.ErrorInfo = errorInfo;
return response;
}
}
Usage:
catch (HttpRequestException hex)
{
return Response.CreateErrorResponse<T>(ExceptionDispatchInfo.Capture(hex)); // should compile (I did not check)
}
You can cast the response to T. EDIT: Added full source code
public class Response
{
public bool Success { get; private set; }
public static Response CreateErrorResponse()
{
return new Response { Success = false };
}
}
public interface ICommand<T> where T : Response
{
Task<T> ExecuteAsync();
}
public abstract class CommandBase<T> : ICommand<T> where T: Response
{
protected abstract Uri BuildUrl();
protected abstract Task<T> HandleResponseAsync();
public async Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new System.Net.Http.HttpClient();
try
{
var response = await httpClient.GetAsync(url);
return null;// await HandleResponseAsync(response);
}
catch (Exception hex)
{
return (T)Response.CreateErrorResponse(); // doesn't compile
}
}
}
public async override Task<T> ExecuteAsync()
{
var url = BuildUrl();
var httpClient = new HttpClient();
try
{
var response = await httpClient.GetAsync(url);
return await HandleResponseAsync(response);
}
catch (HttpRequestException hex)
{
return (T)Response.CreateErrorResponse(); // compiles on liqpad
}
}