creating web api and calling asynchronous - c#

I am creating web api. I want to call in bot messenger framework.
In web api controller i have method :
public async Task<HttpResponseMessage> Get()
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, DateTime.UtcNow.ToString());
return response;
}
In the bot framework i have this code :
public static async Task<List<EventModel>> Response(string uri)
{
//string escapeString = Uri.EscapeDataString(phrase);
string returnValue = string.Empty;
try
{
using (var client = new HttpClient())
{
var message = await client.GetAsync(uri);
if (message.IsSuccessStatusCode)
{
var jsonResponse = await message.Content.ReadAsStringAsync();
jsonResponse = jsonResponse.TrimStart('\"');
jsonResponse = jsonResponse.TrimEnd('\"');
jsonResponse = jsonResponse.Replace("\\", "");
var data = JsonConvert.DeserializeObject<List<EventModel>>(jsonResponse);
return data;
}
else return null;
}
}
catch (Exception exc)
{
return null;
}
}
the problem : client.GetAsync(uri) never ends.
Some help ?

Related

.Net Core - Docker Linux memory usage keeps increasing

I am bit frustrated now what's wrong with my code, and I hope you guys can help me with it, so here are the things I have tried.
so I tried making the HttpClient static, and I tried using the IHttpClientFactory.CreateClient() and I even added this on my .csproj
<ServerGarbageCollection>false</ServerGarbageCollection>
Here is the sample code that I have been doing
public class TestController : BaseController
{
private static HttpClient _httpClient = new();
public TestController()
{
}
[HttpGet("bills")]
public async Task<IActionResult> GetBillsPresentment([FromQuery] GetBillPresentmentQuery query)
{
if (!query.AccountNumber.Contains("-"))
query.AccountNumber = FormatAccountNumber(query.AccountNumber);
var billDetails = await GetBillDetail(query.AccountNumber);
if (billDetails == null)
throw new ProviderProcessException(ProviderErrorCode.INVALID_ACCOUNT_NUMBER);
return Ok(new BillPresentmentVm
{
User = new CustomerDto
{
CustomerName = billDetails.Name
},
Billing = new BillingDto
{
AccountNumber = query.AccountNumber,
DueDate = DateTime.Parse(billDetails.LastReadDate).AddMonths(1),
Outstanding = !string.IsNullOrEmpty(billDetails.Arrears) ? decimal.Parse(billDetails.Arrears) : null
}
});
}
private async Task<ResponseModel> GetBillDetail(string accountNumber)
{
try
{
var payload = new { accno = accountNumber };
string json = JsonConvert.SerializeObject(payload);
var buffer = System.Text.Encoding.UTF8.GetBytes(json);
using var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _httpClient.PostAsync("https://test.com", byteContent);
if (!response.IsSuccessStatusCode)
throw new ProviderProcessException(ProviderErrorCode.BILLING_CYCLE_UNAVAILABLE);
var result = await response.Content.ReadAsStringAsync();
if (result == "Accno not found!") return null;
var data = JsonConvert.DeserializeObject<ResponseModel>(result);
return data;
}
catch (Exception)
{
throw new ProviderProcessException(ProviderErrorCode.BILLING_CYCLE_UNAVAILABLE);
}
}
private static string FormatAccountNumber(string accountNumber)
{
return string.Format("{0:#######-########}", Convert.ToInt64(accountNumber));
}
}
And here's the docker memory usage
The memory usage keeps increasing after a request. Can someone explains me why it is not decreasing?
Thank you very much in advance
I solve this issue using IHttpClientFactory instead of HttpClient.
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
I use some thing like this and it works fine on large amount of requests per second and it use memory in normal way .
namespace BehsaLoyalty.ApiClient
{
public class ApiRepo : IApiRepo
{
private readonly IHttpClientFactory _HttpClientFactory;
public ApiRepo (IHttpClientFactory httpClientFactory)
{
_HttpClientFactory = httpClientFactory;
}
public async Task<ResponseModel> PostMyObject(Myobject model, CancellationToken cancellationToken)
{
HttpClient httpClient = _HttpClientFactory.CreateClient("ApiDestinationURI");
using HttpRequestMessage request = new(HttpMethod.Post, "/blah/blah");
request.Content = new StringContent(JsonSerializer.Serialize(model));
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
response.EnsureSuccessStatusCode();
string createdContent = await response.Content.ReadAsStringAsync();
ResponseModel ResponseReturn = JsonSerializer.Deserialize<ResponseModel>(createdContent);
return ResponseReturn;
}
}
}

Http Get Request not getting any data

I have my Web Api on a production server online and working well in postman and in Xamarin forms so far until I needed to do a Get Request and does not return any data. Infact it stops at the GetAsStringAsync line and does not continue. Instead, it jumps out of the method and then nothing more.
Does any one know what the problem could be? I have checked and made sure my Internet is working and the Uri too.
This is where I am doing my Get in Xamarin forms:
public async Task<List<OfferModel>> AllOffers()
{
var httpclient = new HttpClient();
httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Settings.AccessToken);
//it does not continue after this line, it jumps out of the method instead
var response = await httpclient.GetStringAsync(UrlConstants.offerurl);
var data =JsonConvert.DeserializeObject<List<OfferModel(response);
return data;
}
Solution 1
Can you try access task via awaiter it may be wait until result when responded
public class HttpHelperService
{
public async Task<List<OfferModel>> AllOffers()
{
List<OfferModel> result;
string responseBody;
using (HttpClient client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Settings.AccessToken);
HttpResponseMessage response = client.GetStringAsync(new Uri(UrlConstants.offerurl)).GetAwaiter().GetResult();
result = JsonConvert.DeserializeObject<List<OfferModel>>(response);
}
catch (Exception ex)
{
result = null;
}
return result;
}
}
}
Solution 2
public class MyPage : ContentPage
{
//Here is your page constructor
public MyPage()
{
GetServices(); //--> call here without awaiter
}
}
//Here is your awaiter method
private async void GetServices()
{
LoadingPopupService.Show();
var result = await HttpService.AllOffers();
LoadingPopupService.Hide();
}
//Here is your service.
public async Task<List<OfferModel>> AllOffers()
{
var httpclient = new HttpClient();
httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Settings.AccessToken);
var response = await httpclient.GetStringAsync(UrlConstants.offerurl);
var data =JsonConvert.DeserializeObject<List<OfferModel(response);
return data;
}

Error handling (Sending ex.Message to the client)

I have an ASP.NET Core 1.0 Web API application and trying to figure out how to pass the exception message to the client if a function that my controller is calling errors out.
I have tried so many things, but nothing implements IActionResult.
I don't understand why this isn't a common thing that people need. If there truthfully is no solution can someone tell me why?
I do see some documentation out there using HttpResponseException(HttpResponseMessage), but in order to use this, I have to install the compat shim. Is there a new way of doing these things in Core 1.0?
Here is something I have been trying with the shim but it isn't working:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
Customer c = _customersService.GetCustomerById(id);
if (c == null)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response);
//return NotFound();
}
return new ObjectResult(c);
}
When the HttpResponseException is thrown, I look on the client and can't find the message I am sending anything in the content.
Here is an simple error DTO class
public class ErrorDto
{
public int Code {get;set;}
public string Message { get; set; }
// other fields
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And then using the ExceptionHandler middleware:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
context.Response.ContentType = "application/json";
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
var ex = error.Error;
await context.Response.WriteAsync(new ErrorDto()
{
Code = <your custom code based on Exception Type>,
Message = ex.Message // or your custom message
// other custom data
}.ToString(), Encoding.UTF8);
}
});
});
Yes it is possible to change the status code to whatever you need:
In your CustomExceptionFilterAttribute.cs file modify the code as follows:
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new ContentResult
{
Content = $"Error: {exception.Message}",
ContentType = "text/plain",
// change to whatever status code you want to send out
StatusCode = (int?)HttpStatusCode.BadRequest
};
}
}
That's pretty much it.
If you have custom exceptions, then you can also check for them when grabbing the thrown exception from the context. Following on from that you can then send out different HTTP Status Codes depdending on what has happened in your code.
Hope that helps.
You can create a custom Exception Filter like below
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new JsonResult(exception.Message);
}
}
Then apply the above attribute to your controller.
[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
throw new Exception("Suckers");
return new string[] { "value1", "value2" };
}
}
Rather than raising and catching an exception, how about you simplify your action to:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
var customer = _customersService.GetCustomerById(id);
if (customer == null)
{
return NotFound("Customer doesn't exist");
}
return Ok(customer);
}
I wrote a blog post with some more options such as returning a JSON object instead of text.
Maybe that is helpful. You can return just object and sent for example a BadRequest (HTTP CODE: 400) with your custom object as actual parameter (I just used an interpolated string here) but you can put in anything.
In your client side you can catch that error situation for example with an AJAX error handler.
// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return truckFahrerGeoDataItems;
}
Or an even more cleaner way with IActionResult like that way:
// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return Ok(truckFahrerGeoDataItems);
}
Late to the party but refining the answer .
Define your error response class with minimum below attributes
using Microsoft.AspNetCore.Http;
public class ErrorResponse
{
private readonly RequestDelegate next;
public ErrorResponse(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError;
string result = string.Empty;
object data = new object();
if (ex is ForbiddenException)
{
code = HttpStatusCode.Forbidden;
result = JsonConvert.SerializeObject(new Response<object>(Status.Forbidden(ex.Message), data));
}
else if(ex is BadRequestException){
code = HttpStatusCode.BadRequest;
result = JsonConvert.SerializeObject(new Response<object>(Status.BadRequest(ex.Message), data));
}
else if (ex is NotFoundException)
{
code = HttpStatusCode.NotFound;
result = JsonConvert.SerializeObject(new Response<object>(Status.NotFound(ex.Message), data));
}
else if (ex is UnauthorizedException)
{
code = HttpStatusCode.Unauthorized;
result = JsonConvert.SerializeObject(new Response<object>(Status.Unauthorized(ex.Message), data));
}
else
{
result = JsonConvert.SerializeObject(new Response<object>(Status.InternalServerError(ex.Message), data));
}
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
Next use this class as middleware in startup.cs class
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorResponse));
Now each request and response will go through this class,if an error occurs then error code will be set to true with error code. A sample response like below
data: {}
status: {
code: 404
error: true
message: "No employee data found"
type: "Not Found"
}
I had the same problem and after some research, I found out I could use HttpClient to call my API and read the response easily. HttpClient does not throw any error when the HTTP response contains an error code, but it sets the IsSuccessStatusCode property to false.
This is my function using the HttpClient. I call this from my controller.
public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
{
string uri = apiUrl + url;
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));
return response;
}
}
This is my controller code, where I call the function and read the response and determine whether I have an error or not and respond accordingly. Note that I am checking the IsSuccessStatusCode.
HttpResponseMessage response;
string url = $"Setup/AddDonor";
var postdata = JsonConvert.SerializeObject(donor);
response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
//var headers = response.Headers.Concat(response.Content.Headers);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = true,
message = tnxresult.Message,
statusCode = tnxresult.StatusCode
});
}
else
{
ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = false,
message = rs.Message,
statusCode = rs.StatusCode
});
}
My API returns error messages in JSON. If the call is successful, I am packing the response in JSON too.
The crucial line of code is this one...
var responseBody = await response.Content.ReadAsStringAsync();
It serializes the HTTP content to a string as an asynchronous operation.
After that I can convert my JSON string to an object and access the error/success message and the Status Code too.

MVC request to Web Api

I'm trying to build an MVC that requests through a PCL to a WebApi. I am sending a get requests and getting stuck on the awaiting for the response. Postman returns the correct values. I also don t get exceptions on send. The 3 projects are both on the same solution.
PCL
HttpResponseMessage httpResponse = null;
try
{
httpResponse = await _http.GetAsync( "http://localhost:43818/api/values" );
}
catch (Exception e)
{
var meessage = e.Message;
var stack = e.StackTrace;
}
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
string json = await httpResponse.Content.ReadAsStringAsync( );
}
So the issue is that in the PCL, it's doesn pass the await, it gets stuck.
MVC
var result = apiClient.GetIndex( );
Web Api
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Also, how do i wait in my MVC for the response before rendering the controller view
In your Class library (PCL), Create method GetIndex as below,
public async Task GetIndexAsync()
{
HttpResponseMessage httpResponse = null;
try
{
_http.BaseAddress = new Uri("http://localhost:43818/");
httpResponse = await _http.GetAsync("api/values");
}
catch (Exception e)
{
var meessage = e.Message;
var stack = e.StackTrace;
}
if (httpResponse.StatusCode == HttpStatusCode.OK)
{
string json = await httpResponse.Content.ReadAsStringAsync();
}
}
And In MVC calling method as below,
var result = apiClient.GetIndexAsync().Wait();
which solved both your problems.
Ok so i found the best sollution. Blocking threads is not a very good idea.
This is the fix
PCL
public async Task<HttpResponseMessage> Register()
{
HttpRequestMessage request = new HttpRequestMessage
{
RequestUri = new Uri( _http.BaseAddress, "account/register/" ),
Method = HttpMethod.Post,
Content = new StringContent( "{\"Email\": \"email#yahoo.com\",\"Password\": \"Password!1\",\"ConfirmPassword\": \"Password!1\"}",
Encoding.UTF8,
_contentType
),
};
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await _http.SendAsync( request, CancellationToken.None );
}
catch (Exception e)
{
Debugger.Break();
}
return response;
}
MVC Client
public async Task<ViewResult> Index( )
{
var thisTask = await Api.Register( );
return View();
}

Return model from async task after call to web api

I have a web api which I'm calling (this is working correctly)
I call this like so
public ActionResult Index()
{
var mod = Checksomething();
return View();
}
public async Task Checksomething()
{
try
{
var client = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(new UserLogin { EmailAddress = "SomeEmail#Hotmail.com", Password = "bahblah" }));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("http://localhost:28247/api/UserLoginApi2/CheckCredentials", content);
var value = await response.Content.ReadAsStringAsync();
// I need to return UserProfile
var data = JsonConvert.DeserializeObject<UserProfile[]>(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
My web api passes back a model called UserProfile, I'm having great difficulty trying to return data back to the Index controller, would someone please enlighten me.
You need to change your method signature to use the generic version of Task
public async Task<ActionResult> Index()
{
UserProfile[] profiles = await Checksomething();
if (profiles.Any())
{
var user = profiles.First();
string username = user.FirstName;
// do something w/ username
}
return View();
}
public async Task<UserProfile[]> Checksomething()
{
try
{
var client = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(new UserLogin { EmailAddress = "SomeEmail#Hotmail.com", Password = "bahblah" }));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("http://localhost:28247/api/UserLoginApi2/CheckCredentials", content);
var value = await response.Content.ReadAsStringAsync();
// I need to return UserProfile
return JsonConvert.DeserializeObject<UserProfile[]>(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
The returned Task will be unwrapped and your caller will be given the Result of the Task, which in this case will be UserProfile[]

Categories

Resources