Unit test for web API application with dotnet 6 - .net-core

I have a web API application with JWT authentication, I want to write test for it, I don't know use XUnit or call APIs with HTTP client!
XUnit
[Fact]
public async Task Authenticate_WithValidUsernamePassword_ReturnsToken()
{
//...
}
Http client
[TestMethod]
public async Task Authenticate_WithValidUsernamePassword_ReturnsToken()
{
HttpClient _client =...
var httpResponse = await _client.GetAsync("api/v1/admin/Authenticate?....");
}

Try xUnit like this:
[Fact]
public async Task Authenticate_WithValidUsernamePassword_ReturnsToken()
{
using var client = new HttpClient();
var content = await client.GetStringAsync("/api/v1/admin/Authenticate?....");
bool result = false;
if (content == "foo authentication ok") {
result = true;
}
Assert.True(result, $"foo authentication failed Result={result}");
}

Related

httpclient call is invoked after await keyword in dotnet core

I would like to do some operation which doesn't depend on API response and at the same time I want API to finish its process.
But in my case, API doesn't receive request when postasync is executed.
Instead, Web api receive request after await weatherForeCastdata.
I noticed strange behavior today
when I executed endpoint for first time(both the apis), webapi received request after postasync method. From second time, api receives request after executing await weatherForeCastdata.
I launched applictaion from vs code
browser : chrome
Can anyone help me ?
public async Task<IEnumerable<WeatherForecast>> Get()
{
var rng = new Random();
var weatherForeCastdata = new HttpClientCall<WeatherForecast>(_configuration).PostRequest(_configuration["Services:Payperiod"],new WeatherForecast());
Console.WriteLine("apiinvoked");
var data = await weatherForeCastdata;
//var data1 = await data.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(data);
}
public class HttpClientCall<T> where T : class
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
private readonly IConfiguration _configuration;
internal HttpClientCall(IConfiguration configuration)
{
httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; //Is valid
}
return true;
};
_configuration = configuration;
}
public async Task<string> PostRequest(string apiUrl, T postObject)
{
using (var client = new HttpClient(httpClientHandler))
{
client.DefaultRequestHeaders.Add("ClientId", _configuration["header"]);
Console.WriteLine(apiUrl);
var response = client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter());
var response1=await response;
return await response1.Content.ReadAsStringAsync();
}
}
}

HttpClient Await PostAsync not completed

I have an asmx Web Service and I am using async Task. My problem is whenever I reached on the PostAsync statement it will just end there and fire a result to the browser with an empty result. Which is not I want. I tried passing the httpclient as a parameter to my service class thinking it may solved the issue.
I tried putting ConfigureAwait(false) and it gives a result however I don't want this because I need to return the value to the user. If I use ConfigurAwait(false) it will return an empty result to the browser even if it it still not completed. Am I doing this right? Thanks
in my webmethod
public class WebService1 : WebService
{
HttpClient Client = new HttpClient();
XDocument doc = new XDocument();
[WebMethod]
private async Task<String> Sample1(string a, int b)
{
myServiceClass _ms = new myServiceClass(Client);
var message = await _ms.GetResponseMessageAsync(a,b);
doc = await _ms.ReadResponseAsync(message); // It will not reach here if I don't use ConfigureAwait(false)
return JsonConvert.SerializeObject(doc);
}
}
myServiceClass.cs
public class myServiceClass
{
HttpClient _client;
public myServiceClass(HttpClient client)
{
_client = client;
}
public async Task<HttpResponseMessage> GetResponseMessageAsync(string a, int b)
{
HttpResponseMessage message;
httpcontent = (a,encoding.UTF8,"text/xml"); //This is just a sample content
message = await _client.PostAsync(UrlString, httpcontent); //<= here it stops and return empty result if there is no ConfigureAwait(false).
if (!message.IsSuccessStatusCode)
{
throw new HttpRequestException($"Cannot connect to api: {message.StatusCode} , {message.ReasonPhrase}");
}
return message; // It will not reach here if I don't use ConfigureAwait(false)
}
}

Connect asp.net core web api with Blazor Web assembly like Angular SPA

I am new to Bazor web assembly (Blazor Client).
I have created a project with Asp.net Core web api with Angular Application.
In order to work with asp.net core web api and angular,
I can use the default functionality like
AddSpaStaticFiles
UseSpa
How can I use Blazor webassembly like the angular?
Or
How can replace the existing Angular SPA with Blazor Client?
Some links provided a solution for Blazor assembly preview.
But the same functionality not found on the latest.
https://csharp.christiannagel.com/2019/08/27/blazorserverandclient/
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
Remember that Web Assembly Apps are created to work like another SPA like Angular or React, it means that you create your view presentation or Blazor Web Assembly App in a independent project, then you get the data from some Web Service, for example an Rest API made in .Net Core 3.1, to create the Blazor Web Assembly Project you just go to File -> New -> Project -> Blazor App -> Blazor WebAssembly App, do not choose ASP.NET Core Hosted option, this will attach your project to the .net core backend directly like an MVC Project.
After having your new project created, you just simple need to call your backend end-points with the Built-In Http Client library that comes with .Net Core or you can create your own library using .Net HttpClient and Inject it in your components or pages using Dependency Injection, if you want to create your own library, follow this process:
First Create this HttpObject:
public class HttpResultObject<T>
{
public bool IsSuccessful { get; set; }
public HttpStatusCode HttpResultCode { get; set; }
public T Result { get; set; }
}
Then create your Library Class:
public class MyLibrary : IMyLibrary
{
public string GetApiUri(string server)
{
if (server.Equals("auth"))
return "https://localhost:8080/api/";
return "https://localhost:8080/api/";
}
//Http Get Method Example
public async Task<HttpResultObject<U>> SetAppMethodGetParametersAsync<U>(string server, string method, Dictionary<string, object> parameters, CancellationToken token) where U : class
{
string getParameters = string.Empty;
foreach(var p in parameters)
{
if (p.Value.GetType() == typeof(string))
getParameters = getParameters.Equals(string.Empty) ? "?" + p.Value.ToString() : "&" + p.Value.ToString();
}
var uri = new System.Uri(GetApiUri(server) + method + "/" + getParameters) ;
var response = await CallAppMethodGetAsync(uri, token);
var result = new HttpResultObject<U>()
{
IsSuccessful = response.IsSuccessStatusCode,
HttpResultCode = response.StatusCode,
Result = JsonSerializer.Deserialize<U>(response?.Content?.ReadAsStringAsync()?.Result)
};
return result;
}
private async Task<HttpResponseMessage> CallAppMethodGetAsync(System.Uri uri, CancellationToken token)
{
Console.WriteLine(uri.ToString());
HttpStatusCode responseStatus = HttpStatusCode.BadRequest;
try
{
HttpClient client = new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(240000)
};
HttpResponseMessage response = new HttpResponseMessage(responseStatus);
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = uri,
Method = HttpMethod.Get
};
var authToken = this.GetLocalStorageItem("authToken");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (authToken != null && authToken.GetType() == typeof(string))
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Convert.ToString(authToken));
token.ThrowIfCancellationRequested();
response = await client.SendAsync(request, token);
responseStatus = response == null ? HttpStatusCode.BadRequest : response.StatusCode;
if (response != null && responseStatus != HttpStatusCode.OK && responseStatus != HttpStatusCode.Accepted)
{
HttpResponseMessage result = new HttpResponseMessage(responseStatus)
{
Content = new StringContent(response.Content?.ReadAsStringAsync()?.Result, Encoding.UTF8, "application/json")
};
return response;
}
return response;
}
catch (WebException webException)
{
}
catch (System.Net.Sockets.SocketException socketException)
{
}
catch (HttpRequestException httpRequestException)
{
}
catch (ArgumentException argumentException)
{
}
catch (System.Exception exception)
{
}
return new HttpResponseMessage(responseStatus);
}
}
Now create your ILibrary Interface and declare the Implemented Methods:
public interface IMyLibrary
{
string GetApiUri(string server);
Task<HttpResultObject<U>> SetAppMethodGetParametersAsync<U>(string server, string method, Dictionary<string, object> parameters, CancellationToken token) where U : class;
}
Declare your Dependency Injection in your startup.cs in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyLibrary, MyLibrary>();
}
Now, if you want to you use your library in some Razor Component or Page just inject it like this:
#inject IMyLibrary _myLibrary
#code
{
private async Task MyHttpGetCall()
{
var cts = new CancellationTokenSource();
var result = await _myLibrary.SetAppMethodPostParametersAsync<HttpResultObject<MyCustomObject>>("auth", new Dictionary<string, object>(), cts.Token);
if (result.IsSuccesful)
{
//whatever you want to do
}
}
}
And that is all!, those are the 2 ways to connect your front-end web site developed with Blazor Web Assembly App to your Backend the same way you does with Angular or React.

How can I make http request into startup class to call http get action in same asp.net web api project?

I need to create http request into startup class to call http get action in same asp.net web api project .
I need to create http request into startup class to call http get action in same asp.net web api project
You can try to inject IHostApplicationLifetime into Configure() method , then write the callback for ApplicationStarted that would be triggered when the application host has fully started, and make http request within callback method, like below.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime)
{
//code logic here
IHttpClientFactory _clientFactory = app.ApplicationServices.GetService(typeof(IHttpClientFactory)) as IHttpClientFactory;
lifetime.ApplicationStarted.Register(OnApplicationStartedAsync(_clientFactory).Wait);
//...
}
private async Task<Action> OnApplicationStartedAsync(IHttpClientFactory httpClientFactory)
{
var client = httpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get,
"https://localhost:44377/api/values");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
}
return null;
}
Calling AddHttpClient to register IHttpClientFactory
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// ...
}
Test Result

Passing JWT Token as QueryString to SignalR Hub

Trying to follow the suggestions in the link below to pass a JWT token to my SignalR hub but so far it's not working. In particular, see David Fowler's suggestion on July 22, 2017. https://github.com/aspnet/SignalR/issues/130
My frontend is React so I'm simply adding the token to the querystring as follows where _token has my JWT token value:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/myhub?AUTHORIZATION=" + _token)
.configureLogging(signalR.LogLevel.Information)
.build();
In the ConfigureServices() method of my Startup.cs, I have the following configuration for Jwt tokens:
services.AddAuthentication(options => {
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions => {
jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AzureAdB2C:Tenant"]}/{Configuration["AzureAdB2C:Policy"]}/v2.0/";
jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
jwtOptions.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
if(context.HttpContext.WebSockets.IsWebSocketRequest)
context.Token = context.Request.Query["AUTHORIZATION"];
return Task.CompletedTask;
}
};
});
And this is what my Hub looks like:
[Authorize]
public class MyHub : Hub
{
private IBackendService _backendService;
public MyHub(IBackendService backendService)
{
_backendService = backendService;
}
public async Task SendMessage(string message)
{
// Regular SignalR stuff
// SignalR will now send the message to all connected users...
}
}
Basically, I'm getting the 401 Unauthorized error.
I put a break point where I check to see if the request is a web sockets request but I'm not hitting it. Looks like something in the pipeline is determining that the user is not authenticated.
What am I doing wrong in my code?
You can solve this by using custom middleware to handle grabbing the authentication token from the query string.
public class SignalRQueryStringAuthMiddleware
{
private readonly RequestDelegate _next;
public SignalRQueryStringAuthMiddleware(RequestDelegate next)
{
_next = next;
}
// Convert incomming qs auth token to a Authorization header so the rest of the chain
// can authorize the request correctly
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers["Connection"] == "Upgrade" &&
context.Request.Query.TryGetValue("authToken", out var token))
{
context.Request.Headers.Add("Authorization", "Bearer " + token.First());
}
await _next.Invoke(context);
}
}
public static class SignalRQueryStringAuthExtensions
{
public static IApplicationBuilder UseSignalRQueryStringAuth(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SignalRQueryStringAuthMiddleware>();
}
}
This will try to get the query string value "authToken" and it will set the heads so you can leverage your authentication middleware. You need to call this before the authentication middleware in the pipeline like so:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
app.UseSignalRQueryStringAuth();
app.UseAuthentication();
//...
}
EDIT
on a side note you should only append the token if the user is logged in:
if (accessToken) {
hubUrl += '?authToken' +'=' + accessToken;
}
this._hubConnection = new HubConnectionBuilder()
.withUrl(hubUrl)
.build();
I have also implemented this in my project. The shortest way of doing this is to add a middleware to your Configure method.
app.Use(async (context, next) =>
{
var accessToken = context.Request.Query["access_token"];
if (!string.IsNullOrEmpty(accessToken))
{
context.Request.Headers["authorization"] = "Bearer " + accessToken;
}
await next.Invoke().ConfigureAwait(false);
});
It does the same thing as mentioned in the other answer. It adds the token to the header by reading it from the query string. Of course, you can separate out the implementation of custom middleware in a separate file.

Resources