ASP.NET Core WebAPI HttpClient in integration test - asp.net-core

In my Web API, I have 3 methods:
val1 - set passed value to session
val2 - set passed value to session
diff - returns if both values in session are different
To create a client I use
var webAppFactory = new WebApplicationFactory<Program>();
_httpClient = webAppFactory.CreateDefaultClient();
Then in integration tests, I use:
var responseVal1 = await _httpClient.PutAsync("/val1", content);
var responseVal2 = await _httpClient.PutAsync("/val2", content);
var responseDiff = await _httpClient.GetAsync("/diff");
Problem is that every requests of PutAsync or GetAsync generates new session on the server. Methods work from browser, but don't work from integration test because every request has own HttpContext.Session.
What am I doing wrong?
Appreciate any help.

Related

Prepare / read authenticated user claims from HttpContext.User

var authenticateResult = await HttpContext.AuthenticateAsync("External");
if (!authenticateResult.Succeeded) return BadRequest();
var email = authenticateResult.Principal.FindFirst(ClaimTypes.Email);
var id = _usersService.Create(email.Value);
var claimsIdentity = new ClaimsIdentity("Application");
claimsIdentity.AddClaim(new Claim("id", id.ToString()));
await HttpContext.SignInAsync("Application", new ClaimsPrincipal(claimsIdentity));
In controllers the following line throws Sequence contains no elements in controllers:
var idClaim = HttpContext.User?.Claims.Where(x => x.Type == "id").Single();
I call UseAuthentication and UseAuthorization in Startup and I thought that the first snippet would set cookies and then User would provide access to it on every client request but it doesn't work.
If I put a breakpoint right after SiginInAsync then HttpContext.User contains the expected claim but not in other calls.
HttpContext.Request.Cookies is empty. In browser I see several cookies among them .AspNetCore.External and .AspNetCore.Application plus some antiforgery cookies.
How do I achieve having claims in User for all requests after authentication was done?
Can it be the problem that my front-end runs on a separate port? Probably not.
You could follow the steps below to add updated claims to HttpContext.User:
https://stackoverflow.com/a/65319557/11398810
Then you can get claims in other request:
var idClaim = HttpContext.User?.Claims.Where(x => x.Type == "id").Single();

Asp.net core website intermittently refusing connections

I have an asp.net core 3.0 website. It has a controller that implements an HttpGet function that does some database stuff then returns a Json object (https://localhost:44356/api/runner/match).
I have a console application that uses an HttpClient to hit that url. I launch the site locally and then I launch my console app. About 50% of the time it works. The other 50% of the time I get:
HttpRequestException: No connection could be made because the target machine actively refused it.
SocketException: No connection could be made because the target machine actively refused it.
I'm trying to figure out why my console app's connection is being blocked. I don't know how to start debugging this. I tried to implement a retry on the request, but once I get the exception, I keep getting it. So I think it's something non-deterministic happening in my website, potentially related to SSL?
I'm able to hit the url in Chrome locally just fine.
How do I figure out what is blocking the connection from being made?
Is there any chance this is something IIS Express is doing?
Calling code in console app:
static async Task<List<Deck>> GetMatchData()
{
string baseUrl = "https://localhost:44356/api/runner/match";
using (HttpClient client = new HttpClient())
{
HttpResponseMessage res = null;
res = await client.GetAsync(baseUrl);
Controller function:
[HttpGet("match")]
public async Task<ActionResult> GetMatchup()
{
int count = db.Decks.Count();
Random r = new Random();
int d1 = r.Next(count) + 1; // database ids start at 1 for some reason
int d2 = r.Next(count - 1) + 1;
if (d1 == d2)
d2++;
List<Deck> result = new List<Deck>();
result.Add(await db.Decks.FindAsync(d1));
result.Add(await db.Decks.FindAsync(d2));
if (result[0] == null || result[1] == null)
{
return BadRequest();
}
return Ok(result);
}
try
HttpClient httpClient = new HttpClient();
//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
string baseUrl = "https://localhost:44356/api/runner/match";
httpClient.BaseAddress = new Uri(baseUrl );
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var getResult = await httpClient.GetAsync(baseUrl);

Flutter run multiple http request take much time

I want to ask about increase performance when i do multiple future http request in single page. In case , i want to build a dashboard page. In dashboard, i've 4 endpoints url that return different result in every endpoint and should be shown in dashboard page.
here example code when load data
var client = new http.Client();
Future main() async {
var newProducts = await client.get("${endpoint}/product?type=newly&limit=5");
ProductListResponse newProductResponse = ProductListResponse.fromJson(json.decode(newProducts.body));
var bestSeller = await client.get("${endpoint}/product?type=best-seller&limit=5");
ProductListResponse bestSellerResponse = ProductListResponse.fromJson(json.decode(bestSeller.body));
var outOfStock = await client.get("${endpoint}/product?type=out-of-stock&limit=5");
ProductListResponse outOfStockResponse = ProductListResponse.fromJson(json.decode(outOfStock.body));
var lastRequest = await client.get("${endpoint}/product-request?type=newly&limit=5");
ProductRequestListResponse productRequestResponse = ProductRequestListResponse.fromJson(json.decode(lastRequest.body));
}
every endpoint when i hit manually using postman it takes 200ms for return the result. But when i implement in flutter app, it took almost 2s.
can i improve performance when getting data?
The reason why your code run so slow is that you are making those HTTP requests one by one. Each await will take quite some time.
You can either not use await and implement the logic using callbacks (.then) or you can combine the Futures into one using Future.wait and use await for that combined Future.
Your code will look something like this:
var responses = await Future.wait([
client.get("${endpoint}/product?type=newly&limit=5"),
client.get("${endpoint}/product?type=best-seller&limit=5"),
client.get("${endpoint}/product?type=out-of-stock&limit=5"),
client.get("${endpoint}/product-request?type=newly&limit=5")
]);

.net core 2.2 httpclient Factory not giving Full data as response

I am using .net core 2.2 for my flight listing application and i am using wego api for that. but while i am using the below code for getting flights from wego api i am not getting the complete response, but in postman i am getting full result set at one request.
public async Task<SearchResultMv> GetFlights(FlightParam flightParam, AuthResult auth)
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://srv.wego.com/metasearch/flights/searches/" + flightParam.SearchId + "/results?offset=0&locale=" + flightParam.locale + "&currencyCode=" + flightParam.currencyCode);
request.Headers.Add("Bearer", auth.access_token);
request.Headers.Add("Accept", "application/json");
var client = _httpClient.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", auth.access_token);
var response = await client.SendAsync(request).ConfigureAwait(false);
SearchResultMv json = new SearchResultMv();
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
json = await response.Content.ReadAsAsync<SearchResultMv>().ConfigureAwait(false);
return json;
}
}
Some time I am not getting any result set by the above code. Wego api is not providing any pagination or filtration on this api. so Please help me to achieve this. Thanks for advance.
According the their documentation you need to poll their API to get the results gradually. You also need to increment the offset as the results are returned.
For example, if the first set of results gives you 100 results, the following request should have the offset value set as 100. offset=100.
Documentation:
https://developers.wego.com/affiliates/guides/flights
Edit - Added sample solution
Sample code that polls the API every second until reaching the desired number of results. This code hasn't been tested so you'll need to adjust it to your needs.
const int numberOfResultsToGet = 100;
var results = new List<SearchResultMv>();
while (results.Count < numberOfResultsToGet)
{
var response = await GetFlights(flightParam, auth);
results.AddRange(response.Results);
// update offset
flightParam.Offset += response.Results.Count;
// sleep for 1 second before sending another request
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Change your request to use a dynamic Offset value. You can add the Offset property to the FlightParam class.
var request = new HttpRequestMessage(
HttpMethod.Get,
$"https://srv.wego.com/metasearch/flights/searches/{flightParam.SearchId}/results?" +
$"offset={flightParam.Offset}" +
$"&locale={flightParam.locale}" +
$"&currencyCode={flightParam.currencyCode}");

How to communicate WCF exceptions to WebClient

I have a WCF web service which throws exceptions when invalid data is submitted. The data is submitted via an HTTP Post using the WebClient object.
Here is the code for the web service:
[WebInvoke(UriTemplate = "update", Method = "POST")]
public JsonValue Update(HttpRequestMessage message)
{
var context = new Entities();
dynamic response = new JsonObject();
// in order to retrieve the submitted data easily, reference the data as a dynamic object
dynamic data = message.Content.ReadAs(typeof(JsonObject), new[] { new FormUrlEncodedMediaTypeFormatter() });
// retrieve the submitted data
int requestId = data.requestId;
int statusId = data.statusId;
string user = data.user;
string encryptedToken = data.token;
string notes = data.notes;
// retrieve the request with a matching Id
var request = context.Requests.Find(requestId);
// make sure the request exists
if (request == null)
throw new FaultException("The supplied requestId does not exist.");
// make sure the submitted encrypted token is valid
var token = DecryptToken(encryptedToken);
if (token == null)
throw new FaultException("Invalid security token.");
// TODO: Validate other token properties (e.g. email)?
if (!request.User.UserName.Equals(token.UserName))
throw new FaultException("Invalid security token.");
// additional logic removed ...
}
And here is the code that submits data to the web service:
// use the WebClient object to submit data to the WCF web service
using (var client = new WebClient())
{
client.Encoding = Encoding.UTF8;
// the data will be submitted in the format of a form submission
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
var data = new NameValueCollection();
// prepare the data to be submitted
data.Add("requestId", requestId.ToString());
data.Add("statusId", this.StatusId);
data.Add("token", token.ToString());
data.Add("user", this.User);
data.Add("notes", this.Notes);
// submit the data to the web service
var response = client.UploadValues(this.Address, data);
}
I keep getting an exception with message: "The remote server returned an error: (500) Internal Server Error" at client.UploadValues(this.Address, data);.
Is there a way I can make sure that more detailed information is returned to the WebClient?
Also, how can I make sure that these exceptions (in the WCF service) are logged to the EventLog? (Basically I just need to know what happened).
Take a look at HttpResponseException (namespace Microsoft.ApplicationServer.Http.Dispatcher) - they're the way where you can control the response for error cases. You can specify the status code, and you have control over the HttpResponseMessage, in which you can control the message body.
On the client side, when you call WebClient.UploadValues, wrap that call and catch a WebException. If the service returns a response with a non-successful status code (e.g., 500, 400), the Response property of the WebException will have the body, in which you can read in your client.
Another option is to use HttpClient instead of the WebClient, in which case you can simply look at the HttpResponseMessage directly.