I have an api which will be publicly exposed and have a sandbox. I've written some code in my ResourceFactory so api.sandbox.whatever/whatever will work and also sandbox=true in the arguments will work but this feels like a giant hack. Any better ways to do it?
Here is my code:
public class NinjectResourceFactory : IResourceFactory
{
private readonly IKernel _productionKernel;
private readonly IKernel _sandboxKernel;
public NinjectResourceFactory()
{
_productionKernel = new StandardKernel(new QueryMasterModule());
_sandboxKernel = new StandardKernel(new QueryMasterModule(true));
}
public object GetInstance(Type serviceType, InstanceContext instanceContext, HttpRequestMessage request)
{
string uri = request.RequestUri.ToString();
if (uri.Contains(".sandbox."))
{
return _sandboxKernel.Get(serviceType);
}
else if (uri.Contains("sandbox=true"))
{
request.RequestUri = new Uri(uri.Replace("sandbox=true", ""));
return _sandboxKernel.Get(serviceType);
}
else
{
return _productionKernel.Get(serviceType);
}
}
public void ReleaseInstance(InstanceContext instanceContext, object service)
{
// todo do I need to implement this?
}
}
If it's supposed to be a true sandbox then you don't want the two sites to run in the same process. I would deploy two web sites and let IIS decide which one based on host name. That way the sandbox will be isolated from production, which is the purpose of a sandbox.
Related
I've created a custom library which automatically sets up Polly policies for specific services which depend on HttpClient.
This is done using the IServiceCollection extension methods and the typed client approach. A simplified example:
public static IHttpClientBuilder SetUpFooServiceHttpClient(this IServiceCollection services)
{
return services
.AddHttpClient<FooService>()
.AddPolicyHandler(GetRetryPolicy());
}
public class FooService
{
private readonly HttpClient _client;
public FooService(HttpClient httpClient)
{
_client = httpClient;
}
public void DoJob()
{
var test = _client.GetAsync("http://example.com");
}
}
Note that my real code uses a generic type and an options builder, but I've omitted that part to keep it simple. The purpose of my tests is to confirm that my options builder correctly applies the policies that I want it to apply. For the sake of example here, let's just assume that it's a hardcoded retry policy which I want to test.
I now want to test if this library correctly registers the Polly policies to my injected HttpClient dependencies.
Note
There are many answers to be found online and on StackOverflow where the suggestion is to construct the HttpClient yourself, i.e.: new HttpClient(new MyMockedHandler());, but this defeats my purpose of needing to test if the actual IHttpClientFactory is constructing httpclients with the requested policies.
To that end, I want to test with a real HttpClient which was generated by a real IHttpClientFactory, but I want its handler to be mocked so I can avoid making actual web requests and artificially cause bad responses.
I'm using AddHttpMessageHandler() to inject a mocked handler, but the factory seems to be ignoring that.
Here's my test fixture:
public class BrokenDelegatingHandler : DelegatingHandler
{
public int SendAsyncCount = 0;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
SendAsyncCount++;
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
}
private BrokenDelegatingHandler _brokenHandler = new BrokenDelegatingHandler();
private FooService GetService()
{
var services = new ServiceCollection();
services.AddTransient<BrokenDelegatingHandler>();
var httpClientBuilder = services.SetUpFooServiceHttpClient();
httpClientBuilder.AddHttpMessageHandler(() => _brokenHandler);
services.AddSingleton<FooService>();
return services
.BuildServiceProvider()
.GetRequiredService<FooService>();
}
And here's my test:
[Fact]
public void Retries_client_connection()
{
int retryCount = 3;
var service = GetService();
_brokenHandler.SendAsyncCount.Should().Be(0); // PASS
var result = service.DoJob();
_brokenHandler.SendAsyncCount.Should().Be(retryCount); // FAIL: expected 3 but got 0
}
When I debug the test, the handler's breakpoint is never hit, and the response comes back as a 200 (because it actually connected to the URL, instead of hitting the mocked handler).
Why is my mocked handler being ignored by the http client factory?
Note that I will also accept any answer that allows me to test the policies in another valid way.
I'm aware I can just use a broken URL string but I'm going to need to test specific http responses in my tests.
We had a similar problem few months ago. How to test that the injected HttpClient is decorated with the correct Policies. (We have used a Retry > CircuitBreaker > Timeout policy chain).
We ended up to create several integration tests. We have used WireMock.NET to create a server stub. So, the whole point of this was to let the ASP.NET DI do its magic and then scrutinize the stub's logs.
We have created two base classes which wrapped the WireMock setup (we had a POST endpoint).
FlawlessServer
internal abstract class FlawlessServiceMockBase
{
protected readonly WireMockServer server;
private readonly string route;
protected FlawlessServiceMockBase(WireMockServer server, string route)
{
this.server = server;
this.route = route;
}
public virtual void SetupMockForSuccessResponse(IResponseBuilder expectedResponse = null,
HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
server.Reset();
var endpointSetup = Request.Create().WithPath(route).UsingPost();
var responseSetup = expectedResponse ?? Response.Create().WithStatusCode(expectedStatusCode);
server.Given(endpointSetup).RespondWith(responseSetup);
}
}
FaultyServer
(We have used scenarios to simulate timeouts)
internal abstract class FaultyServiceMockBase
{
protected readonly WireMockServer server;
protected readonly IRequestBuilder endpointSetup;
protected readonly string scenario;
protected FaultyServiceMockBase(WireMockServer server, string route)
{
this.server = server;
this.endpointSetup = Request.Create().WithPath(route).UsingPost();
this.scenario = $"polly-setup-test_{this.GetType().Name}";
}
public virtual void SetupMockForFailedResponse(IResponseBuilder expectedResponse = null,
HttpStatusCode expectedStatusCode = HttpStatusCode.InternalServerError)
{
server.Reset();
var responseSetup = expectedResponse ?? Response.Create().WithStatusCode(expectedStatusCode);
server.Given(endpointSetup).RespondWith(responseSetup);
}
public virtual void SetupMockForSlowResponse(ResilienceSettings settings, string expectedResponse = null)
{
server.Reset();
int higherDelayThanTimeout = settings.HttpRequestTimeoutInMilliseconds + 500;
server
.Given(endpointSetup)
.InScenario(scenario)
//NOTE: There is no WhenStateIs
.WillSetStateTo(1)
.WithTitle(Common.Constants.Stages.Begin)
.RespondWith(DelayResponse(higherDelayThanTimeout, expectedResponse));
for (var i = 1; i < settings.HttpRequestRetryCount; i++)
{
server
.Given(endpointSetup)
.InScenario(scenario)
.WhenStateIs(i)
.WillSetStateTo(i + 1)
.WithTitle($"{Common.Constants.Stages.RetryAttempt} #{i}")
.RespondWith(DelayResponse(higherDelayThanTimeout, expectedResponse));
}
server
.Given(endpointSetup)
.InScenario(scenario)
.WhenStateIs(settings.HttpRequestRetryCount)
//NOTE: There is no WillSetStateTo
.WithTitle(Common.Constants.Stages.End)
.RespondWith(DelayResponse(1, expectedResponse));
}
private static IResponseBuilder DelayResponse(int delay) => Response.Create()
.WithDelay(delay)
.WithStatusCode(200);
private static IResponseBuilder DelayResponse(int delay, string response) =>
response == null
? DelayResponse(delay)
: DelayResponse(delay).WithBody(response);
}
Simple test for Slow processing
(proxyApiInitializer is a instance of a WebApplicationFactory<Startup> derived class)
[Fact]
public async Task GivenAValidInout_AndAServiceWithSlowProcessing_WhenICallXYZ_ThenItCallsTheServiceSeveralTimes_AndFinallySucceed()
{
//Arrange - Proxy request
HttpClient proxyApiClient = proxyApiInitializer.CreateClient();
var input = new ValidInput();
//Arrange - Service
var xyzSvc = new FaultyXYZServiceMock(xyzServer.Value);
xyzSvc.SetupMockForSlowResponse(resilienceSettings);
//Act
var actualResult = await CallXYZAsync(proxyApiClient, input);
//Assert - Response
const HttpStatusCode expectedStatusCode = HttpStatusCode.OK;
actualResult.StatusCode.ShouldBe(expectedStatusCode);
//Assert - Resilience Policy
var logsEntries = xyzServer.Value.FindLogEntries(
Request.Create().WithPath(Common.Constants.Routes.XYZService).UsingPost());
logsEntries.Last().MappingTitle.ShouldBe(Common.Constants.Stages.End);
}
XYZ Server init:
private static Lazy<WireMockServer> xyzServer;
public ctor()
{
xyzServer = xyzServer ?? InitMockServer(API.Constants.EndpointConstants.XYZServiceApi);
}
private Lazy<WireMockServer> InitMockServer(string lookupKey)
{
string baseUrl = proxyApiInitializer.Configuration.GetValue<string>(lookupKey);
return new Lazy<WireMockServer>(
WireMockServer.Start(new FluentMockServerSettings { Urls = new[] { baseUrl } }));
}
I hope this can help you.
I feel fairly confident that I have this wrong (or backwards). I'm looking to determine what environment a request is coming from, so that I can determine what database to access (based on the referrer url).
The end point is a "self-hosted" API on one of our servers, and I'd like to send requests from both development and production to the service, and handle the requests accordingly. I'd like to avoid adding additional frameworks, as well as avoiding having to handle the environment in each action call.
Here's what I have going so far.
public ServiceProvider svc;
public ECTaskService lab;
public BaseAPIController()
{
//I WAS HOPING FOR THE ENVIRONMENT REFERENCE HERE....
svc = new ServiceProvider();
lab = new ECTaskService();
}
My hope was that I could access the filter context below from the constructor above to pass the environment down to the service provider, which would handle the all of the facets of the database / dev environment as needed.
public class LabelServerFilter : ActionFilterAttribute {
public override void OnActionExecuting(HttpActionContext actionContext)
{
Environment(actionContext);
base.OnActionExecuting(actionContext);
}
public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
Environment(actionContext);
return base.OnActionExecutingAsync(actionContext, cancellationToken);
}
public string Environment(HttpActionContext actionContext)
{
try
{
var env = "PRODUCTION";
Uri referrer = actionContext.Request.Headers.Referrer;
if (referrer != null)
{
string clientDomain = referrer.GetLeftPart(UriPartial.Authority);
if (clientDomain.IndexOf("myinternalurldev") > -1)
{
env = "DEVELOPMENT";
}
}
//Console.Write("[" + env + "]");
return env;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message, ex);
}
return "PRODUCTION";
}
}
Thanks in advance!
Is it possible to create one instance of httpclient in Xamarin Forms application in OnStart() and use it everywhere in my application?
Yes you can use the same httpclient for all request in your app. But you will need to take note that if there is API that is having different base URL or headers information, then you will need to create another httpclient for that.
What I do is I have a class to manage the HttpClient instances. If there is no instance that is matching the HttpConfig, it will create and store it. If there is already an existing instance, it will just return it.
Example of code (HttpService is dependency injected):
public class HttpService : IHttpService
{
private static readonly int MAX_CLIENT = 5;
private Dictionary<HttpConfig, HttpClient> mClients;
private Queue<HttpConfig> mClientSequence;
public HttpService()
{
mClients = new Dictionary<HttpConfig, HttpClient>();
mClientSequence = new Queue<HttpConfig>();
}
private HttpClient CreateHttpClientAsync(HttpConfig config)
{
HttpClient httpClient;
if (mClients.ContainsKey(config))
{
httpClient = mClients[config];
}
else
{
// TODO: Create HttpClient...
if (mClientSequence.Count >= MAX_CLIENT)
{
// Remove the first item
var removingConfig = mClientSequence.Dequeue();
mClients.Remove(removingConfig);
}
mClients[config] = httpClient;
mClientSequence.Enqueue(config);
}
return httpClient;
}
...
}
HttpConfig is class where I store BaseUrl, Timeout, Headers, Auth info, etc. You will need to override the Equals method in the class for comparison whether there is existing same config.
public override bool Equals(object obj)
{
// Logic to determine whether it is same config
}
I've an application using SignalR & WebAPI. I've a custom Authorization context based on a token, which I give on each SignalR requests using the QueryString.
I've implement and IUserIdProvider, in order to retrieve my User from the Token.
And finally, I want to call a client method from the server, for a specific User (with is ID), I'm using a HubContext from the GlobalHost.ConnectionManager.
My problem is that my User is never find from my HubContext, but it is from the Hub itself...
Here is my IUserIdProvider implementation
public class SignalRUserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
var token = request.QueryString["token"];
var scope = GetUnprotectedScope(token);
if (scope == null)
return null;
return scope.Id_User.ToString();
}
}
Here is my Hub imlementation
[HubName("notifier")]
public class NotifierHub : Hub
{
public void Notify(string message)
{
Clients.User("1").Notify(message); //When call from a client, this works very well, only the User with the Id = 1 receive the notification
}
}
And finally, I use this to call the client method from my server:
GlobalHost
.ConnectionManager
.GetHubContext<NotifierHub>()
.Clients
.User("1")
.Notify(notification.Message);
// This does nothing...
I'm out of solution at this point, I don't understand what happens, does anyone have already achieve this ?
I've finally figured the issue, but I don't know how to fix it...
The actual issue is simple, the Hub itself has a good context with the clients and everything, but the GlobalHost.ConnectionManager have nothing.
If I change my Hub to something like this:
[HubName("notifier")]
public class NotifierHub : Hub
{
public void Notify(string message)
{
Clients.User("1").Notify(message + " from Hub itself");
GlobalHost
.ConnectionManager
.GetHubContext<NotifierHub>()
.Clients
.User("1")
.Notify(message + " from ConnectionManager");
}
}
My client receive "My message from Hub itself", but never receive "My message from ConnectionManager".
In conclusion, I've a problem with my DependencyInjection... I'm using Structuremap, with this DependencyResover :
public class StructureMapSignalRDependencyResolver : DefaultDependencyResolver
{
private IContainer _container;
public StructureMapSignalRDependencyResolver(IContainer container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
if (serviceType == null)
return null;
var service = _container.TryGetInstance(serviceType) ?? base.GetService(serviceType);
if (service != null) return service;
return (!serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
? _container.GetInstance(serviceType)
: _container.TryGetInstance(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = _container.GetAllInstances(serviceType).Cast<object>();
return objects.Concat(base.GetServices(serviceType));
}
}
My Startup file:
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", RegisterSignalR);
}
public static void RegisterSignalR(IAppBuilder map)
{
var resolver = new StructureMapSignalRDependencyResolver(IoC.Initialize());
var config = new HubConfiguration { Resolver = resolver };
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(config);
}
And finally my Registry...
For<Microsoft.AspNet.SignalR.IDependencyResolver>().Add<StructureMapSignalRDependencyResolver>();
For<INotifier>().Use<SignalRNotifier>();
I have a service class that is being used in an ASP.Net MVC 5 web application and in a console application also. Let's call it MyService. When a MyService object is being instantiated by Ninject to be passed to a controller in a web context, I would like a URL to be resolved using something like this
url = Request.Url.GetLeftPart(UriPartial.Authority) + Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Emailer" });
When MyService is being instantiated by my console app (using Ninject also, but in a non-web context), I would like it to take the url from AppSettings like this:
url = ConfigurationManager.AppSettings["EmailerUrl"];
Is this feasible? I think it might be using WithConstructorArgument() with the proper arguments in NinjectWebCommon.RegisterServices(IKernel kernel) when creating the binding, but I don't know how exactly.
My guess is that the MyService constructor would have to know about the controller class in which it is being injected and be able to call methods from that controller. If no controller class is known, then it would use the app settings.
Of course, I could also set the url parameter within each controller constructor being passed a MyService object, and elsewhere in the console app, but I would prefer not to worry about setting this attribute every time a MyService object is instantiated.
So let's start with:
public interface IUrl
{
Url Url { get; }
}
internal class ControllerBasedUrl : IUrl
{
public ControllerBasedUrl(string controllerName)
{
this.Url = null; // implement
}
public Url Url { get; private set; }
}
internal class AppConfigBasedUrl : IUrl
{
public AppConfigBasedUrl()
{
this.Url = null; // implement
}
public Url Url { get; private set; }
}
There's multiple approaches you can use:
a) make it known to the application whether you're running in a web application or console application, and then use a conditional binding:
var kernel = new StandardKernel();
if (runningInConsoleApplication)
{
kernel.Bind<IUrl>().To<AppConfigBasedUrl>();
}
else
{
kernel.Bind<IUrl>().ToMethod(ctx =>
{
IRequest controllerRequest = ctx.Request.TraverseRequestChainAndFindMatch(x => x.Target.Name.EndsWith("Controller"));
return new ControllerBasedUrl(controllerRequest.Target.Type.Name);
});
}
b) make the binding for url conditional on whether it is injected into controller or not:
var kernel = new StandardKernel();
kernel.Bind<IUrl>().ToMethod(ctx =>
{
IRequest controllerRequest = ctx.Request.TraverseRequestChainAndFindMatch(x => x.Target.Name.EndsWith("Controller"));
if (controllerRequest != null)
{
return new ControllerBasedUrl(controllerRequest.Target.Type.Name);
}
return new AppConfigBasedUrl();
});
both using this extension:
public static class RequestExtensions
{
public static IRequest TraverseRequestChainAndFindMatch(this IRequest request, Func<IRequest, bool> matcher)
{
if (matcher(request))
{
return request;
}
if (request.ParentRequest != null)
{
return request.ParentRequest.TraverseRequestChainAndFindMatch(matcher);
}
return null;
}
}