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);
}
Related
I'm new to RabbitMq and I just followed internet resource as a tutorial. here I have created two separate API for OrderRequest and OrderConsume. from the order request, I'm calling something like this.
IRequestClient<OrderRequest> _client;
public OrderRepo(IRequestClient<OrderRequest> requestClient)
{
_client = requestClient;
}
public async Task<string> GetOrderList(OrderRequest orderRequest)
{
string datas = "";
try
{
using (var request = _client.Create(orderRequest))
{
var response = await request.GetResponse<OrderReponse>();
datas = response.Message.orderName;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
return datas;
}
and from the OrderConsume, I did something like this.
public class OrderConsumer : IConsumer<OrderRequest>
{
public Task Consume(ConsumeContext<OrderRequest> context)
{
OrderReponse request = new();
try
{
var data = context.Message.orderName + " HelloWorld";
request.orderName = data;
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
return Task.CompletedTask;
}
}
When I debugging the code Consume part working fine, but here you can see, I'm not passing any response from there. there for from the request API. I'm getting time out error.
Timeout waiting for response, RequestId: a1670000-a53b-c025-bd0b-08d9d52ca58a.
Actually I don't need any response from Consumer side, and just wanted to pass the request to consumer. I think I need to change my code here?
using (var request = _client.Create(orderRequest))
{
var response = await request.GetResponse<OrderReponse>();
datas = response.Message.orderName;
}
is it correct? but actually i don't know how to do it. please help me to solve this.
If your order command does not require a response, don't use the request client. Just publish the command and forget about it.
IPublishEndpoint _publishEndpoint;
public OrderRepo(IPublishEndpoint publishEndpoint)
{
_publishEndpoint = publishEndpoint;
}
public async Task<string> GetOrderList(OrderRequest orderRequest)
{
string datas = "";
try
{
await _publishEndpoint.Publish<OrderRequest>(orderRequest);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
return datas;
}
Of course, in your original code you were actually using the response, so...
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 sending an object from my web project to my API project within the same solution. I have a class that uses RestSharp and acts as the sender to the API for all services.
Currently, there is one API controller that is receiving the object, but not all of the parameters are being retained and show up with null values via PUT. However, a different controller using the ApiClient's 'PutAsync' receives its own object with all the values intact.
I've even tried changing the method to receive as a POST, but still no success.
Am I missing something, or is there something wrong that is happening with the serialization/de-serialization of the object?
public class UserInfo
{
public int UserId { get; set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string EmailAddress { get; private set; }
}
internal class WebService : IWebService
{
public async Task<bool> UpdateProfile(UserInfo userInfo)
{
try
{
var url = "/User/UpdateProfile";
return await apiClient.PutAsync<UserInfo, bool>(url, userInfo);
}
catch (Exception ex)
{
this.logger.LogError("Error in UpdateProfile", ex);
throw;
}
}
}
RestSharp Setup
internal class ApiClient : IApiClient
{
public async Task<TOut> PutAsync<TIn, TOut>(string url, TIn data) where TIn : new()
{
return await PushData<TIn, TOut>(url, data, Method.PUT);
}
private async Task<TOut> PushData<TIn, TOut>(string url, TIn data, Method method) where TIn : new()
{
var request = new RestRequest(url, method);
request.AddHeader(HttpRequestHeader.Authorization.ToString(), $"Bearer {GetApiAccessToken()}");
request.AddJsonBody(data);
var result = await client.ExecuteAsync<TOut>(request);
if (result.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Unable to push API data");
}
return result.Data;
}
}
Data in the request prior to being sent out, found under the Parameters, are as followed:
Data sent to API UserController
[Produces("application/json")]
[ApiController]
[Authorize]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpPut]
[Route("UpdateProfile")]
public async Task<IActionResult> UpdateProfile([FromBody] ProfileUpdateInfo profileInfo)
{
try
{
var status = await this.service.UpdateProfile(profileInfo);
return Ok(status);
}
catch (Exception ex)
{
return BadRequest("Error in updating profile");
}
}
}
This is the Data that shows up in the parameter:
Data Consumed by API UserController
After reviewing my code again, the problem was that I had the set accessor to private on all string properties, which is why the int property was still coming through.
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; }
}
I want to post data form my code but no data changed . when I debug the code it got me 200 which mean code is good . I tried to test in fiddler and data is changed successfully . I want to know where is the wrong ?
this is my service including url
public static async Task<string> PostOrderAsync(MachinePostModel Order)
{
try
{
var client = new HttpClient();
string data = JsonConvert.SerializeObject(Order);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync("http://94.205.253.150:2016/api/JobOrder", content);
return data;
}
catch
{
return null;
}
}
and this is the step where i send order object
private async Task<bool> AcceptPostOrder()
{
MachinePostModel OrderDetails = new MachinePostModel();
try
{
OrderDetails.ID = 1163;
OrderDetails.joStatus = "should to wait";
OrderDetails.remarks = "hello remarks";
var Client = await Services.MachineTestPostService.PostOrderAsync(OrderDetails);
return true;
}
catch (Exception exc)
{
await MachineTestOrderView.machineobj.DisplayAlert("Alert", exc.Message, "OK");
return false;
}
}
finally my model
public class MachinePostModel
{
public int ID { get; set; }
public string remarks { get; set; }
public string joStatus { get; set; }
}
Get link to check if data is changed
http://94.205.253.150:2016/api/JobOrder/Get?regId=1&driverID=35&status=All%20Status
There are some mistakes in your code. Firstly, decorate your action with HttpPost attribute and add a parameter for sent complex object.
[HttpPost]
private async Task<bool> AcceptPostOrder(MachinePostModel Order)
{
MachinePostModel OrderDetails = new MachinePostModel();
try
{
OrderDetails.ID = 1163;
OrderDetails.joStatus = "should to wait";
OrderDetails.remarks = "hello remarks";
var Client = await Services.MachineTestPostService.PostOrderAsync(OrderDetails);
return true;
}
catch (Exception exc)
{
await MachineTestOrderView.machineobj.DisplayAlert("Alert", exc.Message, "OK");
return false;
}
}
And, there is no routing definition as api/JobOrder, so I assume that you are trying to post the data AcceptPostOrder method. Change the request endpoint;
var response = await client.PostAsync("http://94.205.253.150:2016/api/AcceptPostOrder", content);