Search code examples
c#reactjs.netasp.net-web-apiasp.net-apicontroller

How to get details of error from .NET catch to React.JS?


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

Solution

  • 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.  
    }