In Memory HTTP server Asp.net WebAPI - asp.net-web-api

I am trying to understand how the self host configuration based Integration Tests are running.
In the code below, Should I be registering my config with the WebApiConfig. Registering or not seems to make no difference.
Is the full pipeline really being tested or is this an illusion? Since, If I am not using the config and the routes defined in my API instead declaring my own as I have done here, I am probably just not testing the full pipleine.
Is there any othere way of testing the api completely. The code below is testing a lot of things besides just my pipeline(like the client, SelfHosting etc..). This seems like an overkill to me. Any ideas ?
var config = new HttpSelfHostConfiguration("http://localhost:9090/");
config.Routes.MapHttpRoute("Default", "{api}/{controller}/{id}", new { id = RouteParameter.Optional });
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
MyApiProject.WebApiConfig.Register(config);
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
using (var client = new HttpClient())
{
using (var response = client.PostAsync("http://localhost:9090/api/login",
new FormUrlEncodedContent(new List<KeyValuePair<string,string>> { new KeyValuePair<string, strin("Foo","Bar)}), CancellationToken.None).Result)
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
using (var response = client.GetAsync("http://localhost:9090/api/login").Result)
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
server.CloseAsync().Wait();
}

If you just want to test your controllers, you can write more targeted unit tests to test them. If you want to test the full pipeline your code looks fine except that instead of using a selfhost, you can just use HttpServer saving the network overhead. Also, if you are testing the full pipeline it is better to use the routes that you have in your actual app rather than adding a new route as that would be testing routing as well.
Also, refer to this blog post by Youssef for some ideas on testing your web APIs.

Related

CORS error with UnityWebRequest from ASP.NET Web API 6.0 with CORS enabled

I'm working on a WebGL game with Unity in which I am attempting to call a custom built ASP.NET Web API that's built specifically to work with the game. I've added the code I thought was necessary to get CORS working for making the API requests from the browser, but I keep getting the same error: "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."
I've tried several different variations of how to implement CORS with the API in the program.cs script. I tried:
builder.Services.AddCors(p => p.AddPolicy("corspolicy", build =>
{
build.WithOrigins("*").AllowAnyMethod().AllowAnyHeader();
}));
And
builder.Services.AddCors(o =>
{
o.AddPolicy("corspolicy", build =>
build.WithOrigins("*")
.AllowAnyMethod()
.AllowAnyHeader()
});
Of course, I also included:
app.UseHttpsRedirection();
app.UseCors();
app.UseAuthorization();
app.MapControllers();
app.Run();
According to the documentation and the tutorials I followed, it looks like I added the correct code in the correct places, but when I publish the API to Azure and try to call it from the WebGL game, I always get that same error that I listed above.
I also tried using the AddDefaultPolicy() with just UseCors(), but that had the same result.
I've tried plugging the URL into Postman and HTTPie along with the header: "Origin": "http://127.0.0.1:5500", to mimic the game running on my local machine, and when I include that, it does return an "Acces-Control-Allow-Origin" header in the response set to either "*" or "http://127.0.0.1:5500" (I've tried both), depending on what I used in WithOrigins().
In Unity I'm making the API call like this:
public void MakeAPIRequest()
{
_webRequest = CreateRequest($"https://apiurl...");
_APICallType = APICallType.login;
_webRequestAsyncOperation = _webRequest.SendWebRequest();
_webRequestAsyncOperation.completed += GetRequestAsyncOperation_completed;
}
private UnityWebRequest CreateRequest(string path, RequestType type = RequestType.GET, object data = null)
{
var request = new UnityWebRequest(path, type.ToString());
if (data != null)
{
var bodyRaw = Encoding.UTF8.GetBytes(JsonUtility.ToJson(data));
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
}
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
return request;
}
I'm currently at a loss for how to get this working, so if anyone here has any advice about what to try or look into, I would really appreciate it.

How to implement versioning for Token endpoint in Web API 2

I have a Asp.Net Web API 2 using Token based authentication (OAuth2).
I have implemented Web API versioning using aspnet-api-versioning.
So now I have three different versions of my API. It's really great, I can now change V3 without affecting the current API.
But the /token endpoint is not versioned because it is not in my controller. It's in the Providers.
I searched but couldn't find anything helpful.
We can register more than one token endpoint in the Startup.Auth.cs
So here's what I did:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
AllowInsecureHttp = true, //Allow HTTP to send username password.
};
app.UseOAuthBearerTokens(OAuthOptions);
OAuthOptionsV3 = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/V3/Accounts/Token"),
Provider = new ApplicationOAuthProvider2(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
AllowInsecureHttp = true, //Allow HTTP to send username password.
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptionsV3);
So now I have different token endpoint for each version.
I'm not familiar with this particular setup, but this looks like middleware. There isn't quite enough information here to provide you with a specific answer, but your goals should be achievable in one of a few ways:
Option 1 - Use the Conventions API
If you authorization endpoint is actually a controller (though I think it is not), you can use the Conventions API like so:
services.AddApiVersioning(options =>
{
options.Conventions.Controller<OAuthController>().IsApiVersionNeutral();
}
Conventions was specifically meant to deal with a scenario where a controller might be externally defined and you don't have any control over the source code.
Option 2 - Use a Custom Convention
Middleware could create actions dynamically. As long as actions are actually produced, then you can use a custom IControllerConvention. You would be passed the ControllerModel which contains the actions you need to version. Assuming this is the correct behavior, you'd be looking for matching actions in the source model and then you can apply it to the controller conventions with something like:
public class MyConventions : IControllerConvention
{
public bool Apply(IControllerConventionBuilder controller, ControllerModel controllerModel)
{
var method = // TODO: resolve the target method from controllerModel
if (method == null)
{
return false;
}
controller.Action(method).IsApiVersionNeutral();
return false;
}
}
Option 3 - In Middleware
If this is pure middleware, API versioning isn't directly supported there. You can, however, support versioning on your own if the pipeline is composed properly. Specifically, API Versioning must come before other parts of middleware that need it. This usually happens automatically, but if you need to control registration, you need to change your setup to handle it manually like this:
services.AddApiVersioning(options => options.RegisterMiddleware = false);
// ... inside application setup
services.UseApiVersioning();
The API Versioning middleware doesn't really do much of anything special. It merely adds a pipeline feature. As long as that's before your other middleware, it will be available downstream like this:
var feature = context.Features.Get<IApiVersioningFeature>();
// the raw, unparsed API version, if any
var rawApiVersion = feature.RawApiVersion;
// the parse API version; will be null if no version is specified
// or the value cannot be parsed
var apiVersion = feature.ApiVersion;
// TODO: enforce versioning policies within the middleware
Option 4 - Use the API Explorer
If none of the previous approaches will work for you, you can leverage the API Explorer extensions for API Versioning to build your configuration (as above) from discovered APIs. This would have the advantage of not being hardcoded or require changes every time you release a new version.
Your application startup configuration would change to something like this:
public void Configure(IApplicationBuilder app, IApiVersionDescriptionProvider provider)
{
foreach (var description in provider.ApiVersionDescriptions)
{
var options = new OAuthAuthorizationServerOptions()
{
TokenEndpointPath = new PathString($"/api/{description.GroupName}/Accounts/Token"),
Provider = new ApplicationOAuthProvider2(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
AllowInsecureHttp = true,
};
app.UseOAuthBearerTokens(options);
}
}

PostAsync hanging in Xamarin Forms works on emulator but hangs on actual Mobile phone

I have Xamarin Forms project where I'm trying to POST and GET data to/from a Web API but when I'm making an async/await call, it works on the emulator (not without its original problems!) but when I try it on my actual phone mobile (Samsung S8+), it just hangs indefinitely.
Note that I'm only concentrating on the Android part right now, not iOS, not that the problem should make any difference in either.
This is the code I'm using:
IDataService.cs
Task<TResponse> PostDataAsync<TRequest, TResponse>(string uri, TRequest data)
where TRequest : class
where TResponse : class;
DataService.cs:
public async Task<TResponse> PostDataAsync<TRequest, TResponse>(string
additionalUri, TRequest data)
where TRequest : class
where TResponse : class
{
return await WebClient
.PostData<TRequest, TResponse>
(string.Concat(this.Uri, additionalUri), data);
}
WebClient.cs
using (var client = new HttpClient())
{
var jsonData = JsonConvert.SerializeObject(data);
using (var response = await client.PostAsync(
uri,
new StringContent(jsonData,
Encoding.UTF8,
"application/json" )))
{
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(content);
}
}
}
Method 1:
LoginPageViewModel.cs
public DelegateCommand SignInCommand => _signInCommand ??
(this._signInCommand = new DelegateCommand(SignInCommandAction));
private async void SignInCommandAction()
{
try
{
....
var user = await this._dataService
.PostDataAsync<LoginRequestDto,
LoginResponseDto>(#"Accounts/Login", loginRequestDto);
....
}
...
}
Method2:
LoginPageViewModel.cs
public DelegateCommand SignInCommand => _signInCommand ??
(this._signInCommand =
new DelegateCommand(async () => await SignInCommandAction()));
private async Task SignInCommandAction()
{
try
{
....
var user = await this._dataService
.PostDataAsync<LoginRequestDto,
LoginResponseDto>(#"Accounts/Login", loginRequestDto);
....
}
...
}
The PostDataAsync works with both methods when I call my local web API i.e. http://10.0.2.2/MyApp/api/ but both methods still hangs when calling external my web service from web provider i.e. http://myapp-123-site.atempurl.com/api/ which is a temp url for testing purpose.
The same apply to my GetDataAsync which is not demonstrated in question but I just thought I'd mention it.
Based on the above, you would think that my async/await code is correct since it works when calling the local web api but then what's causing it to hang when calling the remote web api.
As mentioned, I did enable my INTERNET permission in the manifest.
Any suggestions welcomed?
Thanks.
UPDATE-1:
Note that I've just tried to call a GET opertation within the same function and this is working in the emulator but hanging with the actual mobile.
using (var client = new HttpClient())
{
using (var response = await client.GetAsync(uri))
{
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert
.DeserializeObject<TResponse>(content);
}
}
}
UPDATE-2:
This is somehow working and I have no idea why! The only thing that comes to mind is that I upgraded my libraries. This included PRISM which may have been at the source of the problem but I have no idea.
Sorry I can't provide more details. I could role back my code and try to see if it's hanging again but I just don't have the time to go and experiment some more considering the amount of time I've already spent on this. Sorry.
The requested url is an IP or a domain name.
If it is ip, only the IP of the public network can be accessed by devices on multiple network segments.
If it is a domain name, it needs to support the domain name resolution service.
If you do not have these environments for a while, you need the IP of the device and the IP of the server on the same network segment.
The PostDataAsync works with both methods when I call my local web API i.e. http://10.0.2.2/MyApp/api/ but both methods still hangs when calling external my web service from web provider i.e. http://myapp-123-site.atempurl.com/api/ which is a temp url for testing purpose.
From this phenomenon , the reason should be the temp url. From this domain name (myapp-123-site.atempurl.com) can not find the right local IP (10.0.2.2).And when you test in local network , I guess this will work.However the network of actual mobile can be not the same with local network , such as using 3G/4G network , then this will not working.

Protecting webapi with IdentityServer and Autofac - can't get claims

I'm trying to protect my webapi with IdentityServer and OpenID Connect using Autofac. I'm using OWIN. But for some reason I can't get claims of the user. It seems that AccessTokenValidation is not triggered at all. That makes me think there is something wrong in the order of my declarations at my startup. Here is my startup.
public class Startup {
public void Configuration(IAppBuilder appBuilder) {
// Add authentication
this.AddAuthentication(appBuilder);
HttpConfiguration config = new HttpConfiguration();
var container = CreateAutofacContainer();
var resolver = new AutofacWebApiDependencyResolver(container);
config.DependencyResolver = resolver;
WebApiConfig.Register(config);
config.EnsureInitialized();
// Register config - you can't add anything to pipeline after this
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(config);
appBuilder.UseWebApi(config);
}
private static IContainer CreateAutofacContainer() {
var autofacBuilder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
// Register your Web API controllers.
autofacBuilder.RegisterApiControllers(assembly);
// For general logging implementation
autofacBuilder.RegisterType<ConsoleLogger>().As<ILogger>();
// Create empty usage context to be filled in OWIN pipeline
IUsageContext usageContext = new RuntimeUsageContext();
autofacBuilder.RegisterInstance(usageContext).As<IUsageContext>().SingleInstance();
// We need to get usage context builded
autofacBuilder.RegisterType<OIDCUsageContextProvider>().InstancePerRequest();
var container = autofacBuilder.Build();
return container;
}
private void AddAuthentication(IAppBuilder app) {
var options = new IdentityServerBearerTokenAuthenticationOptions();
options.Authority = "MYAUTHORITY";
options.RequiredScopes = new[] { "openid", "profile", "email", "api" };
options.ValidationMode = ValidationMode.ValidationEndpoint;
app.UseIdentityServerBearerTokenAuthentication(options);
// Add local claims if needed
app.UseClaimsTransformation(incoming => {
// either add claims to incoming, or create new principal
var appPrincipal = new ClaimsPrincipal(incoming);
// incoming.Identities.First().AddClaim(new Claim("appSpecific", "some_value"));
return Task.FromResult(appPrincipal);
});
}
I'm using hybrid flow and api is called from SPA-application. I've verified (by calling my identity server's endpoint directly) that access token is valid and there are claims available. I also downloaded IdentityServer.AccessTokenValidation project and attached it as a reference. When I set some breakpoints to methods in that project, they never get called. That is why I think there is something wrong with my startup and OWIN pipeline.
I've declared UsageContext in my startup. It is a class I'm using to collect claims and some configuration settings - to be injected to actual controllers. I think it would be nice way to handle this, so in controllers there is always valid UsageContext available.
I've read a lot of samples and examples but still haven't found exactly same situation. I'll appreciate any attempts to point me into right direction.
Regards,
Borre
Could it be your registration of UsageContext as a Singleton? You mention this class contains claims, so this object should be resolved once pr http request - shouldn't it?
It turned out that there was some mysterious line in AccessTokenValidation - library that didn't work. I use that library to get claims. After changing the line everything seemed to work.
So basically my question is closed now and stuff works. But I'm still not totally convinced this is the right way to do this.
Thanks John for your comments!

Using AWS API/SDK to Register new EC2 Instances with Existing Elastic Load Balancer - is it possible?

I'm working on using the .Net SDK to help automate the deployment of an application into Windows EC2 instances. The process I want to achieve is:
Create a new EC2 instance - this
"bootstraps" itself by loading in
the new application version using a
service.
Ensure the new instance is in the
'running' state
Run some simple acceptance tests on
the new instance.
Register the new instance with an
existing Elastic Load balancer that
has an instance running the old
version of the application.
When the new instance is registered
with the load balancer, de-register
the old instance.
Stop the old EC2 instance.
I've managed to get steps 1 and 2 working, and I'm pretty confident about 3 and 6.
To do this I've been writing a simple C# console app that uses the AWS .Net SDK v1.3.2 to make the various API calls.
However, when I get to step 4 I cannot get the new instance registered with the load balancer. Here is my code:
public IList<Instance> PointToNewInstance(string newInstanceId)
{
var allInstances = new List<Instance>();
using (var elbClient = ClientUtilities.GetElbClient())
{
try
{
var newInstances = new List<Instance> {new Instance(newInstanceId)};
var registInstancesRequest = new RegisterInstancesWithLoadBalancerRequest
{
LoadBalancerName = LoadBalancerName,
Instances = newInstances
};
var registerReponse = elbClient.RegisterInstancesWithLoadBalancer(registInstancesRequest);
allInstances = registerReponse.RegisterInstancesWithLoadBalancerResult.Instances;
var describeInstanceHealthRequest = new DescribeInstanceHealthRequest
{
Instances = newInstances
};
DescribeInstanceHealthResponse describeInstanceHealthResponse;
do
{
describeInstanceHealthResponse = elbClient.DescribeInstanceHealth(describeInstanceHealthRequest);
} while (describeInstanceHealthResponse.DescribeInstanceHealthResult.InstanceStates[0].State == "OutOfService");
_log.DebugFormat("New instance [{0}] now in service - about to stop remove old instance", newInstanceId);
if (allInstances.Any(i => i.InstanceId != newInstanceId))
{
elbClient.DeregisterInstancesFromLoadBalancer(new DeregisterInstancesFromLoadBalancerRequest
{
Instances = allInstances.Where(i => i.InstanceId != newInstanceId).ToList(),
LoadBalancerName = LoadBalancerName
});
foreach (var instance in allInstances.Where(i => i.InstanceId != newInstanceId).ToList())
{
_log.DebugFormat("Instance [{0}] has now been de-registered from load-balancer [{1}]", instance.InstanceId, LoadBalancerName);
}
}
}
catch (Exception exception)
{
_log.Error(exception);
}
}
return allInstances.Where(i => i.InstanceId != newInstanceId).ToList();
}
The code just freezes at this line:
var registerReponse = elbClient.RegisterInstancesWithLoadBalancer(registInstancesRequest);
When I looked in more detail at the documention (relevant documentation here) I noticed this line:
NOTE: In order for this call to be
successful, the client must have
created the LoadBalancer. The client
must provide the same account
credentials as those that were used to
create the LoadBalancer.
Is it actually possible to use the API to register new instances with an existing load balancer?
All of that is easy to implement. Use Auto Scaling. Use API.
As Roman mentions, it sounds like Auto Scaling is a good way for you to go, it may not solve all of your problems but its certainly a good starting point:
-an auto scaling group can be tied to a load balancer, e.g. ill have x healthy instances
-new instances are automatically added to the load balancer (no traffic will be sent until it passed the health check)
-you can define custom health checks, such as ping http://hostname/isalive just have your instance respond to these requests once its passes step 3
-you can define scaling policies but by default if you're over capacity the oldest instances will be killed
-you don't mention the use case of the app but if you don't want a public facing address you can use an internal load balancer that doesn't take any traffic, just looks after the health check
-where possible you should always use least privilege principles for security, with your method you're going to have to give every instance a lot of power to control other instances, whether through mistake or abuse this can go wrong very easily

Resources