I just learn about Web API in-memory hosting right now, It's new stuff for me. I download the Web API self hosting nuget package and just configure the Routing for Web API and reference the Web API project to In Memory hosting project. And everything is working, but I don't know how in-memory can recognize any controller classes in my Web API project. Is there anyone who can explain me about this?
private HttpServer _server;
private string _url = "http://api.mydomain.com/";
private HttpClient _client;
public PortalWebApiInMemoryTest()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
_server = new HttpServer(config);
_client=new HttpClient(_server);
}
Basically in Web API for controller probing to occur the assembly having the controllers should be loaded into memory. In your case if your controllers are defined in the same assembly as your in-memory tests, then the assembly would be scanned for all types which implement System.Web.Http.Controllers.IHttpController.
If your controllers are defined in an assembly different from your tests, then you might need to do additional stuff to make sure to have that assembly be loaded into memory. One example is to create a custom IAssembliesResolver like in the following sample:
http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/CustomAssemblyResolverSample/ReadMe.txt
Related
I attempted to reproduce the method described in this great article by Andrew Lock. However, I am unable to get this running in a .NET core 1.1 console application. When the appsettings.json file is changed and saved, the changes are not reflected in the application without restarting it.
There are multiple files involved, so I created the smallest example I could come up on github. I also provided details in the README.MD file on github.
Any help in resolving this would be most appreciated. Please keep in mind I am new to .NET core, and not an experienced developer. And this is my first question on stackoverflow... Thanks in advance!
The key thing to understand is scope.
There are three scopes in ASP.NET Core - transient, scoped, and singleton. IOptionsSnapshot is configured as a scoped service.
In ASP.NET Core, a scope is started for every request, so every request, you would get a new instance of IOptionsSnapshot, with updated configuration values.
In the example you provided, you are creating an IServiceProvider, and are fetching an instance of IMyService directly from the top level provider:
IServiceCollection services = new ServiceCollection();
Startup startup = new Startup();
startup.ConfigureServices(services);
IServiceProvider serviceProvider = services.BuildServiceProvider();
while (true)
{
var service = serviceProvider.GetService<IMyService>();
var reply = service.DoSomething();
Console.WriteLine(reply);
}
Essentially, you're always using the same scope for every request to the service provider, so you're always getting the same instance of IOptionsSnapshot. Effectively, if you never create a new scope, all of your scoped services become singletons!
The way to fix this is to create a new scope each time you fetch the service:
IServiceCollection services = new ServiceCollection();
Startup startup = new Startup();
startup.ConfigureServices(services);
IServiceProvider serviceProvider = services.BuildServiceProvider();
while (true)
{
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetService<IMyService>();
var reply = service.DoSomething();
Console.WriteLine(reply);
}
}
This also becomes important if you're doing things like creating an EF Core DbContext outside the context of a request in ASP.NET Core app (or in a console app). Always create a new scope before accessing services from the service provider!
P.S. I've created a pull request to fix your sample :)
I have a .NET Web API project. I was able to add Swagger by installing the Swashbuckle NuGet package. Swagger now makes a nice web page that provides documentation and ability to test by going to this url:
http://localhost:20475/product-catalog-api/swagger/ui/index
This part works great. However, when I add FluentValidation and set it up in the WebApiConfig.cs, all of the requests to my site get high-jacked and instead of seeing the Swagger html page, I only get a JSON with an empty response.
I used this article by Matthew Jones to set up FluentValidation:
http://www.exceptionnotfound.net/custom-validation-in-asp-net-web-api-with-fluentvalidation/
Here is what my web api config looks like:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Filters.Add(new ValidateModelStateFilter());
config.MessageHandlers.Add(new ResponseWrappingHandler());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: Version + "/{action}",
defaults: new {controller = "Main"}
);
FluentValidationModelValidatorProvider.Configure(config);
}
I could trigger the FluentValidation manually in every web service method, but I would like to have a generic solution like Matthew Jones did with the Filters and MessageHandlers. Basically what I want to do is say IF the request is for swagger-ui then do not route through FluentValidation.
I ended up adding this code inside ResponseWrappingHandler. There may be a more elegant way, but this worked for me. This causes the swagger page request to kick out of the FluentValidation hook.
if (request.RequestUri.AbsolutePath.Contains("swagger"))
{
return response;
}
I am using the HttpServer class to test my web api. In my application, I have a custom IHttpModule that handles some URL rewriting. I need this module to process the requests for my web api as well. Here is my code that I use to create the HttpServer object.
var config = new HttpSelfHostConfiguration(ServerUrl);
RouteConfig.RegisterRoutes(config.Routes);
FilterConfig.RegisterWebApiFilters(config.Filters);
var httpServer = new HttpServer(config);
Can someone tell me the obvious thing I am missing that I need to do to register my module with the server?
AFAIK you can't use HttpModules with a self hosted WebAPI server. I think what you need is a MessageHandler.
Http Message Handlers
DelegatingHandler (MSDN)
I'm currently evaluating WebAPI and NancyFx for a new project about to start. I've managed to get Nancy to self host from a test assembly (by itself it uses asp.net hosting).
Is there any way to do the same with Web API? I would like to keep the web api project hosted on IIS, but i would like to spin it up from my test assembly, so i can run tests against it.
I have found some blogposts on how to use Autofac to scan controllers from another assembly (seems a little backwards only to get hosting from another assembly to work, but if it can be done, i guess that would be an option), but i would like to keep using Structuremap ioc for this project.
Managed to get it working with help from Mark Jones link. This is what i ended up with in my test assembly.
private static HttpSelfHostServer _server;
[BeforeTestRun]
public static void Setup()
{
var config = new HttpSelfHostConfiguration(Settings.TestUri);
WebApiConfig.Register(config); //map routes
IocConfig.Bootstrap(config); //configure dependency injection
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
[AfterTestRun]
public static void TearDown()
{
_server.CloseAsync().Wait();
}
I have an integration test which I wanted to use as the basis of testing my WebAPI controllers with.
Initially I thought I would have to set-up WebAPI in self-host mode and carry-out end-to-end tests over local Http.
However I realised later by looking at the tests in the WebApiContrib project that its possible to set up an HttpClient with an HttpServer set-up with the correct service route to the WebAPI controller. I seems I can unit test the controllers without setting up WebApi in self-host mode. I can put in any domain name in the request on the client and HttpClient seems to auto-magically bind to the correct controller.
Is there any Http transport happening here, using some local interprocess comms or purely 'seeing' that the server is in the same app domain and thus using reflection?
What is happening under the hood for this to happen?
code:
[Test]
public void Test_WebApi_Controller()
{
Assembly.Load("myproj.Web");
var prodServiceMock = new Mock<IProductService>();
ObjectFactory.Initialize(x => x.For<IProductService>().Use(prodServiceMock.Object));
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default", "webapi/{controller}/{id}", new { id = RouteParameter.Optional });
config.ServiceResolver.SetResolver(new WebApiDependencyResolver());
var server = new HttpServer(config);
var client = new HttpClient(server);
var response = client.GetAsync("http://anything.com/webapi/product").Result;
}
HttpClient has a pluggable pipeline model. Normally when you new up a HttpClient you get a HttpClientHandler instance as the default request processor. That HttpClientHandler is the one actually does the HttpWebRequest. HttpClientHandler derives from HttpMessageHandler.
By no concidence HttpServer also derives from HttpMessageHandler. So in this example the HttpServer is being passed to the HttpClient instance to provide it's request processing. By passing a HttpMessageHandler to the constructor of HttpClient you are telling HttpClient to use the provided handler instead of the default one. If you look at WebRequestHandler in System.Net.Http.WebRequest you will see this is derived from HttpClientHandler and adds some extra functionality that is specific to the Windows Desktop OS.
This means when you make a request to the HTTPClient it is delivered directly to the HttpServer message handler and then processed as it normally would be on the server.