Sending Proper Response from web api - c#

I am trying to send the proper response from Web API i.e. If any error send error else send the content. The code flow for the same is as follow.
[HttpPost]
public IActionResult GetInfo([FromBody] InfoModel info)
{
try
{
var result = new Info().ProcessInfoResponse(info);
if (result==null)
return BadRequest();
else
return Ok(result);
}
catch (Exception e)
{
Log.Error("some exception", e);
return StatusCode(500, e.Message);
}
}
and from middle layer i.e. from Info class we are having different method with there own returning type and from here we are calling the third party APIs which are in another class.
public InfoResponse ProcessInfoResponse(InfoModel info)
{
try
{
var result = serviceLayer.Post<InfoModel>(info);
if (result != null)
{
// Do Something
}
else
{
Log.Error("some error");
return null;
}
}
catch (Exception ex)
{
Log.Error("some error");
return null;
}
}
public InfoRequest ProcessInfoRequest()
{
}
And in service layer we are calling the third party api like below
public HttpResponseMessage Post<T>(T parm) where T : class
{
try
{
_client.DefaultRequestHeaders.Clear();
var postTask = _client.PostAsync("some third party url", Serialize<T>(parm));
postTask.Wait();
if (postTask.Result.IsSuccessStatusCode)
{
return postTask.Result;
}
else
{
Log.Error("some error in service layer");
}
}
catch (Exception ex)
{
Log.Error("some error in service layer");
}
return default(HttpResponseMessage);
}
So my question is how can return exceptions/errors if there are any and if there is no exceptions/error then send the response as it is. This is possible by keeping middle layer returning type as is
Right now if there are no errors then I am able to send the response properly, as my middle layer is getting the expected returning type.
The issue is my middle layer methods has own returning type which is causing me to send the exception/error as is. Because I am not able to map it to proper class OR same class.
I was thinking will add new Property under all returning classes/types which will refer to the exception class, then will bind the exception/error details to that class. This will save doing lot of code changes in all places.
Any help on this appreciated !

Why not create a custom response object so that:
public IActionResult<MyCustomResponseObject> GetInfo([FromBody] InfoModel info)
public class MyCustomResponseObject
{
public string Message { get; set; }
public object Content { get; set; }
public enum State { get; set; }
}

Related

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

Propagate exception message from service to controller .NET

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

The WebAPI not working when sending data as json

I'm just started learn WebAPI,
When i'm trying to call my Api/TessterFunction and send data parameter as JSON ( {"Test":"TEST"} ) i got this response "No HTTP resource was found that matches the request",
But when trying to call it and send the data as query string (http ://localhost/myProject/myApi/TesterFunction?Test="TEST") it's work and Get Done.
[HttpPost]
[Route("TesterFunction")]
public HttpResponseMessage TesterFunction(string Test)
{
try
{
myClass myObject= new myClass();
if (myObject.myStordProcedure(CompanyCode))
{
return Request.CreateResponse(HttpStatusCode.OK, "Done");
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "SP not executed");
}
}
catch(Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
}
}
That won't work because your web api method only accepts string parameter.
What you could do is;
Add a class where the properties will be bound
public class ReceiveModel{
public string Test {get;set;}
}
Then replace your web api method to use ReceiveModel parameter.
[HttpPost]
[Route("TesterFunction")]
public HttpResponseMessage TesterFunction(ReceiveModel model)
{
// see the property here
Console.WriteLine(model.Test);
try
{
myClass myObject= new myClass();
if (myObject.myStordProcedure(CompanyCode))
{
return Request.CreateResponse(HttpStatusCode.OK, "Done");
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "SP not executed");
}
}
catch(Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, e);
}
}

Showing custom error message on web browser

I have created an ASP.NET Web API which calls a java web server to retrieve data. When the java web server is down, i want the Web API to show an error message: {"ErrorMessage:" Server is down} What are the codes that i should add to achieve the custom error message to be shown on the browser?
Here are my codes:
RestfulClient.cs
public class RestfulClient
{
private static HttpClient client;
private static string BASE_URL = "http://localhost:8080/";
static RestfulClient()
{
client = new HttpClient();
client.BaseAddress = new Uri(BASE_URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<string> addition(int firstNumber, int secondNumber)
{
try
{
var endpoint = string.Format("addition/{0}/{1}", firstNumber, secondNumber);
var response = await client.GetAsync(endpoint);
return await response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
//What do i have to code here?
}
return null;
}
}
AdditionController.cs
public class Temp
{
public string firstNumber { get; set; }
public string secondNumber { get; set; }
public string sum { get; set; }
}
public class AdditionController : ApiController
{
private RestfulClient restfulClient = new RestfulClient();
public async Task<IHttpActionResult> Get(int firstNumber, int secondNumber)
{
var result = await restfulClient.addition(firstNumber, secondNumber);
var resultDTO = JsonConvert.DeserializeObject<Temp>(result);
return Json(resultDTO);
}
}
Someone please do help me, thank you so much.
If you catch an exception of type Exception and then decide the server you called is down, that would not be true. Something may go wrong within your own code before you call the other service or after the other service has returned successfully. Therefore, you need to be careful how you make that decision.
Having said that it is still hard to say when you can make that decision with confidence: Is the calling service returning the correct message etc.
Anyhow, you can do something similar to this:
try
{
// ...
}
catch (System.Net.WebException ex)
{
if (ex.Status == System.Net.WebExceptionStatus.ConnectFailure)
{
// To use these 2 commented out returns, you need to change
// your method's return type to Task<IHttpActionResult>
// return Content(System.Net.HttpStatusCode.ServiceUnavailable, "Unavilable.");
// return StatusCode(System.Net.HttpStatusCode.ServiceUnavailable);
return "Unavailable"
}
}
catch(Exception ex)
{
// You could be here because something went wrong in your server,
// or the server you called which was not caught by the catch above
// because it was not WebException. Make sure to give it some
// thought.
// You need to change
// your method's return type to Task<IHttpActionResult> or
// just return a string.
return StatusCode(System.Net.HttpStatusCode.InternalServerError);
}

Converted POST FromBody to HttpRequestMessage - Need to Desearialize Body of Request

I have working REST code that accepts POST messages using FromBody. The parameter received is the object I need to work with. Now, I need to examine the authorization in the header. I think have this figured out by switching the parameter from the object to an HttpRequestMessage. Of course, now the content of the message must be converted to the original object and I'm having difficulty figuring it out.
Here is the original method:
[HttpPost]
public IHttpActionResult Post([FromBody] CardStatusRoot cardStatus)
{
try
{
if (cardStatus == null)
{
return BadRequest("Card data not provided");
}
if (cardStatus.Data.TransactionType.ToLower() == "card")
{
//... Process;
}
}
catch (Exception ex)
{
try
{
// Log the failure to fund the card
}
catch { }
return InternalServerError();
}
return Ok();
}
New Code, using HttpRequestMessage:
[HttpPost]
public IHttpActionResult Post(HttpRequestMessage request)
{
// Get the authentication from the header
var encoding = Encoding.GetEncoding("UTF-8");
var authValue = encoding.GetString(Convert.FromBase64String(request.Headers.Authorization.Parameter));
var validAuthorization = ConfigurationManager.AppSettings["ValidKey"];
if (authValue != validAuthorization)
{
return BadRequest("Not Authorized");
}
// This does NOT compile - Need help converting request.Content to a CardStatusRoot object
CardStatusRoot cardStatus = (CardStatusRoot)request.Content.ReadAsStringAsync().Result;
... Same as first method
}
How do I convert the content of the request to a CardStatusRoot object?
ApiController has access to the current request via the Request property.
[HttpPost]
public IHttpActionResult Post([FromBody] CardStatusRoot cardStatus) {
try {
HttpRequestMessage request = this.Request;
if (cardStatus == null) {
return BadRequest("Card data not provided");
}
if (cardStatus.Data.TransactionType.ToLower() == "card") {
//... Process;
}
} catch (Exception ex) {
try {
// Log the failure to fund the card
}
catch { }
return InternalServerError();
}
return Ok();
}
That said, this question feels more like an XY problem.
You should be looking into
Authentication Filters in ASP.NET Web API 2
Global Error Handling in ASP.NET Web API 2
Just use the previous signature method to compute with data. You can directly access headers value like this
Request.Headers.Authorization.Parameter
Where Request is the object provided in ApiController for each request.

Categories