how to handle Third party API - c#

I have my api created, which internally calls third party api (for which i will refer their api). when their api returns success result (status code 200) and data, i am saving it to database and myapi will also returns 200 status code.
But when their api returns statuscode other than 200 (like- statusCode 401, statusCode 403) then how to handle that in myapi?
plus, in this case myapi should return 200 statuscode or should return the same statusCode which their api returns, if yes How to do that.?
i need to show different toasters on client side based on what response their api returns in angular? how to do that.
i am new to asp.net core web api so can someone help me.
here is my api -
public async Task<IActionResult> AddStudent(student stud)
{
Student Student = await _empRepo.AddOrUpdateStudent(stud);
return new OkObjectResult(student)
}
In the Repo -
public async Task<object> AddOrUpdateStudent(student stud)
{
var response= await testapi.getexternalstudentdata(student.Id);
if(response is Student)
{
//save data in database
// return the same object which saved in DB
}
else{
// what should i do here to return whatever error i am getting to client in asp .net core 3.1
}
}
calling third party api-
public async Task<object> getexternalstudentdata(int StudId)
{
var relativePath = "url"
var response = await SendAsync(HttpMethod.Get, relativePath);
if (!response.IsSuccessStatusCode) return response;
var responseString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Student>(responseString);
}
here is sendAsync Method -
private async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relativePath)
{
var request = new HttpRequestMessage(method, _httpClient.BaseAddress + relativePath);
return await _httpClient.SendAsync(request);
}

Related

httpClient.PutAsync() not updating, 415 Unsupported media type

I am building a Rest API and a Rest Client, the api url is https://localhost:44341 and the client url is https://localhost:44305/, specifically I want to be able to edit pages in the client for a small custom cms.
Anyway, to view a page in the client, I do this:
public async Task<IActionResult> Edit(long id)
{
Page page = new Page();
using (var httpClient = new HttpClient())
{
using var response = await httpClient.GetAsync("https://localhost:44341/api/Pages/" + id);
string apiResponse = await response.Content.ReadAsStringAsync();
page = JsonConvert.DeserializeObject<Page>(apiResponse);
}
return View(page);
}
And it works, I get the actual page data from the API, however the PUT method in the client is not working, this is it:
[HttpPost]
public async Task<IActionResult> Edit(Page page)
{
using (var httpClient = new HttpClient())
{
using var response = await httpClient.PutAsync("https://localhost:44341/api/Pages/" +
page.Id, new StringContent(page.ToString()));
string apiResponse = await response.Content.ReadAsStringAsync();
}
return Redirect(Request.Headers["Referer"].ToString());
}
When I submit the form for the above method it just redirects to the previous request but the changes aren't saved.
Here's the put method from the api:
[HttpPut("{id}")]
public async Task<ActionResult> PutPage(long id, Page page)
{
if (id != page.Id)
{
return BadRequest();
}
context.Entry(page).State = EntityState.Modified;
await context.SaveChangesAsync();
return NoContent();
}
When I inspect using breakpoints, I can see that the response in the POST method says 415 unsupported media type
The 415 unsupported media type status code means that
The server is refusing to service the request because the entity of
the request is in a format not supported by the requested resource for
the requested method.
When you use new StringContent(page.ToString()) then the media type for the StringContent created defaults to text/plain.
You need to send content in json format:
var content = new StringContent(JsonConvert.SerializeObject(page, Encoding.UTF8, "application/json");
using var response = await httpClient.PutAsync($"https://localhost:44341/api/Pages/{page.Id}", content);

Capturing and caching headers for each request

We are limiting access to an enterprise system by forcing the client to make their CRUD calls through our application, and then our application will forward that very same request to its destination, saving the header information.
Client makes a request to an ApiController
We pass the request to the service layer
The service layer forwards the request its intended enterprise system destination.
To elaborate on the points above:
The client issues a request against this:
[HttpGet]
[Route("opportunities({id:guid})")]
[Route("opportunities")]
public async Task<HttpResponseMessage> GetOpportunity()
{
var query = Request.RequestUri.AbsolutePath.Split('/').Last() + Request.RequestUri.Query;
var response = await _opportunityService.GetOpportunity(query);
return response;
}
The service method GetOpportunity is defined as:
public async Task<HttpResponseMessage> GetOpportunity(string query)
{//at the line below is where i want to send the same headers that were passed in originally at step 1
var response = Client.Instance.GetAsync(Client.Instance.BaseAddress + query); //this is just using HttpClient to make this call
var responseType = response.Result.StatusCode;
if (responseType == HttpStatusCode.NotFound)
return new HttpResponseMessage
{
StatusCode = responseType
};
return await response;
}
How do we save the header information from Step 1?
By using the following middleware I have been able to grab ALL header information; however, I am not sure on how to cache them or make them available to the service layer:
public class HeaderAuthenticationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var headers = actionContext.Request.Headers;
}
}
You are basically acting as a proxy. I see a few possible options.
One would be to pass the Original Request into the service as an explicit dependency
[HttpGet]
[Route("opportunities({id:guid})")]
[Route("opportunities")]
public async Task<HttpResponseMessage> GetOpportunity() {
var response = await _opportunityService.GetOpportunity(this.Request);
return response;
}
and extract the information there
public async Task<HttpResponseMessage> GetOpportunity(HttpRequestMessage Request) {
//at the line below is where i want to send the same headers that were passed in originally at step 1
var query = Request.RequestUri.AbsolutePath.Split('/').Last() + Request.RequestUri.Query;
var headers = Request.Headers;
var url = Client.Instance.BaseAddress + query;
//create new request and copy headers
var proxy = new HttpRequestMessage(HttpMethod.Get, url);
foreach (var header in headers) {
proxy.Headers.Add(header.Key, header.Value);
}
var response = await Client.Instance.SendAsync(proxy);//This is an assumption.
var responseType = response.StatusCode; //Do not mix blocking calls. It can deadlock
if (responseType == HttpStatusCode.NotFound)
return new HttpResponseMessage {
StatusCode = responseType
};
return response;
}
If you do not want to mix the layers and concerns you can extract the needed information into your own model and pass that to the service in order to recreate the needed request.

HttpClient GetAsync not working as expected

When testing my web API with Postman my API get executes fine!
When it comes to running the code with HttpClient in my client application the code executes without error but without the expected result on the server.
What could be happening?
From my client application:
private string GetResponseFromURI(Uri u)
{
var response = "";
HttpResponseMessage result;
using (var client = new HttpClient())
{
Task task = Task.Run(async () =>
{
result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
});
task.Wait();
}
return response;
}
Here is the API controller:
[Route("api/[controller]")]
public class CartsController : Controller
{
private readonly ICartRepository _cartRepo;
public CartsController(ICartRepository cartRepo)
{
_cartRepo = cartRepo;
}
[HttpGet]
public string GetTodays()
{
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Add")]
public string GetIncrement()
{
var cart = new CountedCarts();
_cartRepo.Add(cart);
return _cartRepo.GetTodaysCarts();
}
[HttpGet]
[Route("Remove")]
public string GetDecrement()
{
_cartRepo.RemoveLast();
return _cartRepo.GetTodaysCarts();
}
}
Note these API calls work as expected when called from Postman.
You shouldn't use await with client.GetAsync, It's managed by .Net platform, because you can only send one request at the time.
just use it like this
var response = client.GetAsync("URL").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var dataObjects = response.Content.ReadAsAsync<object>().Result;
}
else
{
var result = $"{(int)response.StatusCode} ({response.ReasonPhrase})";
// logger.WriteEntry(result, EventLogEntryType.Error, 40);
}
You are doing fire-and-forget approach. In your case, you need to wait for the result.
For example,
static async Task<string> GetResponseFromURI(Uri u)
{
var response = "";
using (var client = new HttpClient())
{
HttpResponseMessage result = await client.GetAsync(u);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
static void Main(string[] args)
{
var t = Task.Run(() => GetResponseFromURI(new Uri("http://www.google.com")));
t.Wait();
Console.WriteLine(t.Result);
Console.ReadLine();
}
Simple sample used to get page data.
public string GetPage(string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
string page = response.Content.ReadAsStringAsync().Result;
return "Successfully load page";
}
else
{
return "Invalid Page url requested";
}
}
I've had a problem with chace control when using httpclient.
HttpBaseProtocalFilter^ filter = ref new HttpBaseProtocolFilter();
filter->CacheControl->ReadBehavior = Windows::Web::Http::Filters::HttpCacheReadBehavior::MostRecent;
HttpClient^ httpClient = ref new HttpClient(filter);
I'm not really sure what the expected results are or what results your getting at all so this is really just a guessing game right now.
When I POST something using HttpClient I found adding headers by hand seemed to work more often than using default headers.
auto httpClient = ref new HttpClient();
Windows::Web::Http::Headers::HttpMediaTypeHeaderValue^ type = ref new Windows::Web::http::Headers::HttpMediaTypeHeaderValue("application/json");
content->Headers->ContentType = type;
If I don't do these 2 things I found, for me anyways, that half the time my web requests were either not actually being sent or the headers were all messed up and the other half of the time it worked perfectly.
I just read a comment where you said it would only fire once, that makes me think it is the cachecontrol. I think what happens is something (Windows?) sees 2 requests being sent that are the exact same, so to speed things up it just assumes the same answer and never actually sends the request a 2nd time

How to set HttpResponse to HttpResponseMessage in Web API

I have a asp.net web API application hosted using OWIN. The web API application uses an external library which depends on System.Web and writes its response on System.Web.Response object. I have set a dummy object to HttpContext.Current and after that the I expect the external library would set the response at HttpRequest. Then I need to know how I could transfer the result from HttpRequest to HttpRequestMessage so that the web API method could process the result.
Here is some sample code:
public HttpResponseMessage GetTest()
{
HttpResponseMessage responseMessage = new HttpResponseMessage();
HttpResponse httpResponse = new HttpResponse(new StreamWriter(new MemoryStream()));
httpResponse.Write("From HttpResponse");
return responseMessage;
}
I have written some text using the HttpResponse.Write() method, now I need to move the result from HttpResponse to HttpResponseMessage.
I believe what you are looking for is IHttpActionResult see link:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/action-results
Here is an example of how to create a HttpResponseMessage:
public IHttpActionResult Get()
{
HttpResponseMessage responseMessage = "From HttpResponse";
return new ResponseMessageResult(responseMessage);
}
Try:
return Request.CreateResponse(HttpStatusCode.OK, "{data to return here}")
You can try like below:
if (yourCondition)
{
return Request.CreateResponse<Employee>(HttpStatusCode.OK, obj);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Not Found");
}

Using async/await and returning Task<HttpResponseMessage> From ASP.NET Web API Methods

I have a Portable Class Library (PCL) method like this:
public async Task<string> GetLineStatuses()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
{
return response.GetResponseStream().ReadAllText();
}
}
My ASP.NET Web Api method looks like this:
public async Task<HttpResponseMessage> Get()
{
HttpResponseMessage response = new HttpResponseMessage();
string statuses = await service.GetStatuses();
response.Content = new StringContent(statuses);
return response;
}
What are the implications of returning a Task in Web API. Is this allowed? The only reason I want to use await is so I can use a Portable Class Library (PCL). What is the best practice? Should I have a syncronous version of my method and an asyncronous version? What are the performance and code readability and maintainability implications?
Also would I have the same effect if I returned Task<string> rather than Task<HttpResponseMessage>?
Async and await are perfectly acceptable in ASP.NET. Here's a Scott Handselman video demoing it: http://www.asp.net/vnext/overview/aspnet/async-and-await
"Also would I have the same effect if I returned Task<string> rather than Task<HttpResponseMessage>?"
Not really sure what you mean by this. The Task is like a container for the object, so a Task<string> would contain your string result and a Task<HttpResponseMessage> would contain your HttpResponseMessage result... Is that what you mean? I think either method is perfectly acceptable. If you just need the string, then go with that. No point in returning more than you need.
as alternative:
public static async Task<string> CallGET(string requestUri, string id = "")
{
string responseData;
using (var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true }))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Uri.TryCreate(new Uri(baseURI), $"{requestUri}{(string.IsNullOrEmpty(id) ? string.Empty : $"/{id}")}", out Uri fullRequestUri);
using (var response = await client.GetAsync(fullRequestUri))
{
responseData = await response.Content.ReadAsStringAsync();
}
return responseData;
}
}
and call would be:
var getListUsersResult = Utils.CallGET($"/v1/users").Result;
var resultset= JsonConvert.DeserializeObject(getListUsersResult, typeof(List<UsersDTO>)) as List<UsersDTO>;
UserDTO r = users.Where(d => d.Name.ToLower().Contains("test")).FirstOrDefault();
and another call for one item:
var getUser = Utils.CallGET($"/v1/users", $"{USER_ID}").Result;
var getUserResponse = JsonConvert.DeserializeObject(getUser, typeof(UserDTO)) as UserDTO;

Categories

Resources