System.AggregateException on GetStringAsync in asp.net - c#

I am trying to call an API like this:
var client = new HttpClient();
client.DefaultRequestHeaders.Add("apiKey", Token);
**var result = await client.GetStringAsync(GetUrl($"accounts/{accountID}/menu?skip=0&limit=1"));**
var menuList = JsonConvert.DeserializeObject<List<Menu>>(result);
but getting System.AggregateException on GetStringAsync, error CS0103: The name 'InnerExceptionCount' does not exist in the current context
and Exception Message
One or more errors occurred. (Response status code does not indicate success: 404 (Not Found).)
I see that this is returned in result
Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}
I tried to make call using postman with same url and apikey in header and I see the results.
Can you please suggest what is wrong with above code. Api key expects only an apiKey in header.
Thanks.

Anytime that an async call fails you'll get an AggregateException, what's important is the inner exception within that.
It looks like your inner exception is 404 Not Found, which means that you're not calling the correct URL.
You said it's working in postman, that's great. To find the root cause I suggest the following:
Start Fiddler
Make the call through postman, view the request in Fiddler
Make the call through your C# code, view the request in Fiddler
Comparing the Postman request against the C# request should tell you where the error is.

Related

C# API call from MarketStack and print values - Error 403

I would like to make a successful API call, then print the values in order to see if it works. My main goal is to analyze the data, after I can make a successful API call, and build a systematic strategy for trading.
System.Net.Http.HttpRequestException: "Response status code does not indicate success: 403 (Forbidden)
namespace marketstacktest
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
var options = Options.Create(new MarketstackOptions() { ApiToken = "secretTokenHere" });
var marketstackService = new MarketstackService(options, NullLogger<MarketstackService>.Instance);
var appleSymbol = "AAPL";
var fromDate = DateTime.Now.AddDays(-200);
var toDate = DateTime.Now;
//error at the await System.Net.Http.HttpRequestException: "Response status code does not indicate success: 403 (Forbidden)."
List<Marketstack.Entities.Stocks.StockBar> stock = await marketstackService.GetStockEodBars(appleSymbol, fromDate, toDate);
foreach (var stock_i in stock)
{
Console.WriteLine($"close: {stock_i.Close}");
}
}
}
}
In the API manual, which is directly linked from the github, it gives information about all of the error codes. The relevant ones here are these two:
Code
Type
Description
403
https_access_restricted
HTTPS access is not supported on the current subscription plan.
403
function_access_restricted
The given API endpoint is not supported on the current subscription plan.
Their class library on github is just wrapping a json REST api. Every call to the API is just an http request, returning data as json objects. The 403 error indicates that your request was accepted as a valid request, but intentionally rejected by the server for some reason. And according to the docs, the error was because your account is not allowed access to either https or to the type of request.
Their free-tier subscription only includes end-of-day data, which is what you requested, so it wouldn't make sense for that not to be allowed. So, your app is almost certainly making an https call.
I went to the examples at the very beginning of their quick start guide, and chose the end-of-day example to match your app, and clicked on the link. It worked, and gave a bunch of json records. But, the request they made was using 'http' not 'https'.
Changing the requst to 'https' elicited a 403 response with this content (formatted for readability):
{
"error":
{
"code": "https_access_restricted",
"message": "Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."
}
}
At this point we have enough to be almost certain that this is your issue. The final thing is to go look up how to turn https requests off in their class library. To avoid having to go through the code, I checked the help at the bottom of the page one more time, and found this (formatted for readability):
var options = Options.Create(new MarketstackOptions(){
ApiToken = apiKey,
MaxRequestsPerSecond = 3,
Https = true
});
Welp. This should probably be in their first example, since that's what people are most likely to try first, but it's not. So, to stop trying to make http requests, you just need to set the Https option to false in your code. You just need to add that to the options in your code, like so:
var options = Options.Create(new MarketstackOptions(){
ApiToken = "secretTokenHere",
Https = false
});
I will leave the testing to you, but from the browser test, we know that the request should work, unless there's a bug in their library. Given the information that was available, this is almost certainly the issue.

How can i retrieve the http response status code from an url?

I am automating a test for a page that contains a URL that needs to be then tested.
I created a method that I believed was giving me the http status code:
public string ContentUrlHttpRequest()
{
HttpWebRequest protocolWebRequest = (HttpWebRequest)WebRequest.Create(ContentUrl());
protocolWebRequest.Method = "GET";
HttpWebResponse response = (HttpWebResponse)protocolWebRequest.GetResponse();
return response.Headers.ToString();
}
ContentUrl() is another method i created to find the element on the page with the url to be tested and gets it's value.
I have also tried return response.StatusCode.ToString(); but the response i received was "OK".
I know that the response from that url needs to be = 200. I have this assertion that compares the response from the ContentUrlHttpRequest() to the expected results (200):
Assert.AreEqual("200", ContentUrlHttpRequest(), "The Url is not live. Http response = " + ContentUrlHttpRequest());
The response i am getting from ContentUrlHttpRequest() is not the status code but:"Date: Mon, 03 May 2021 09:07:13 GMT".
I understand why it is happening, it is getting the header of the page that is searching. But how could I get the status code? Is it possible with Selenium? Is there something wrong with my method and instead of Headers I need to use something different?
Unfortunately i am not able to provide with the urls that i am testing, or the platform with the url as they are confidential. Hopefully my issue is clear and you guys can give me some guidance.
You are not returning the response status code. You are returning the headers.
You should replace the return statement with this:
return ((int)response.StatusCode).ToString();
I guess you should use response.Status.ToString(); instead of response.Headers.ToString();
But the status contains not only the number like 200 or 401 but also text.
So if you are going to use response.Status.ToString(); you should Assert.True(ContentUrlHttpRequest().contains("200"))
Or you can use response.StatusCode.ToString(); this will give you the status number itself String without additional texts.

System.Text.Json.JsonException: The input does not contain any JSON tokens

I'm just trying to use a Http POST method in a Blazor app through
public async Task CreateUnit(UnitEntity unit)
{
await _http.PostJsonAsync<UnitEntity>("api/units", unit);
}
_http and myObject have been defined elsewhere, but I'm getting this weird error. Can anyone help? This is the closest thing I could find elsewhere: https://github.com/dotnet/runtime/issues/30945.
The full error message is
System.Text.Json.JsonException: The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
And it here's the stack
Another reason this error could pop up, as it did for me, is simply because the API endpoint doesn't exist because it was misspelled.
I got a similar error in Program.cs Main method CreateHostBuilder(args).Build();:
System.FormatException: 'Could not parse the JSON file.'
JsonReaderException: The input does not contain any JSON tokens.
Expected the input to start with a valid JSON token, when isFinalBlock
is true. LineNumber: 0 | BytePositionInLine: 0.
For me it turned out to be the local secrets.json file that not contained a valid json object.
https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0&tabs=windows#secret-manager
Because of this I could not see any errors in Git or rollback to a working commit since the file is not checked in.
Solved by adding an empty object to the file via Visual Studio - right click the project in solution explorer and select Manage User Secrets:
{
}
https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0&tabs=windows#manage-user-secrets-with-visual-studio
In my case the code was doing this:
var json = await response.Content.ReadAsStringAsync();
var result = JsonObject.Parse(json); // threw the exception mentioned in the question
Why did that happen? That's because json value was an empty string "". Parse fails with an empty string.
Fixed it doing this simple change:
var json = await response.Content.ReadAsStringAsync();
var result = string.IsNullOrEmpty(json) ? null : JsonObject.Parse(json);
i had similar issue and the problem to check if the json string you are readying is empty, null, or bad formatted. debug to the code line where you are reading data into string before deserialize or serialize call.
I got this error when communicating between two APIs.
request = await req.DeserializeRequestBodyAsync<MyDto>(jsonSerializerOptions);
Turned out the code below did not actually send any values:
httpRequestMessage.Content = JsonContent.Create(myDto);
var httpClient = _clientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
I had to manually specify:
await httpRequestMessage.Content.LoadIntoBufferAsync();
Like this:
httpRequestMessage.Content = JsonContent.Create(myDto);
await httpRequestMessage.Content.LoadIntoBufferAsync();
var httpClient = _clientFactory.CreateClient();
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
For me, this error occurred when calling FindByNameAsync of UserManager.
Sounds silly, but the database connection string in the appsettings was wrong!
Late answer - but I ran into this using Blazor WebAssembly with Browser Link (trying to get Hot Reload to work). Turns out it's an issue loading the appsettings and Browser Link was expecting the secrets file. I fixed by right clicking the Server project and copy/pasting my appsettings values into the secrets file.
In my case, I was passing the id of my object along with the object itself in the url of the put request to an API and faced the same exception. It turned out that it was not necessary to pass the id (as it was retrieved from the object itself, in fact it was not present in the method signature). Removing the id solved the problem.
This error occurred when communicating between client and web API.
API code:
public async Task<IActionResult> PostAsync(object review)
{
return Ok();
}
Client code:
var res = await _client.PostAsJsonAsync("api/reviews", review);
if (res.IsSuccessStatusCode)
{
var myObject = await res.Content.ReadFromJsonAsync<MyObject>(); //this line threw mentioned error
}
Turned out that the API endpoint was returning something different compared to what I was trying to read from JSON i.e. I was trying to read MyObject but API was returning ActionResult
In my case database column was marked not null and I was passing null in API.

Response Code is 0 when using Status404NotFound

I'm using AspNetCore 1.1.3 to create some endpoints and swagger to check them.
When using Resonse codes:
Status401Unauthorized
Status403Forbidden
Swagger tells me the response code is 401/403 like i would expect.
But when i use Status404NotFound swagger shows 0 as the response code and body is:
{
"error": "no response from server"
}
I'm using the following lines to return status code and body. There is no other change.
Response.StatusCode = StatusCodes.Status404NotFound; // or Status403Forbidden
return new JsonResult("{\"statusCode\":404}"); // 403
Is this expected behaviour? I would like to use the response codes in my application for error handling and 404 returning empty statuscode/body is not really useful.

Web API call works but gives exception The view 'xxx' or its master was not found or no view engine supports

Strange one here, code calls the method, and the method grabacat is executed on the server (I debug it and step through right to the end). The code returns to the client, but the response it received was 500 Internal Server Error with the above message. So it's saying it couldn't find the web API method that it just called successfully.
using (var response = await client.PostAsXmlAsync("cats/grabacat", mycatprefs))
{
if (response.IsSuccessStatusCode) // 500 cats/grabacat not found
Controller code:
[Route("~/api/cats/grabacat")]
[HttpPost]
public async Task GrabACat()
{
}
After debugging, if I change it to public async Task<SomeObject> GrabACat() then it works OK. This lets me return an object back to the client. However I don't want to return anything back; I want it to be equivelent to calling a void method. It will examine the status code to determine if it was successful.
I have got it working by changing GrabACat to Task and returning new object(); but I am not sure why this is required. It seems crude to return an empty object just to get it to work.
Any ideas?
The WebAPI method has a Route attribute like this:
[Route("~/api/cats/grabacat")]
Which means the URL is wrong in the POST request - you are missing the /api prefix:
using (var response = await client.PostAsXmlAsync("api/cats/grabacat", mycatprefs))
//snip

Categories