SelfHosted AspNet WebAPI With Controller Classes In Different Project - asp.net-web-api

I have created a SelfHosted AspNet WebAPI with Visual Studio 2012 (.NET Framework 4.5). I enabled SSL for the WebAPI. It works fine when the controller is defined in the same project.
But when I add a reference of another project, containing controllers, it gives me the following error:
No HTTP resource was found that matches the request URI 'https://xxx.xxx.xxx.xxx:xxxx/hellowebapi/tests/'.
I have created custom classes for HttpSelfHostConfiguration and MessageHandler.
Any help to resolve this problem would be a great time-savor for me.
Thanking in advance.

You can write a simple custom assemblies resolver which makes sure that your referenced assembly is loaded for the controller probing to work.
Following is a nice post from Filip regarding this:
http://www.strathweb.com/2012/06/using-controllers-from-an-external-assembly-in-asp-net-web-api/
Sample:
class Program
{
static HttpSelfHostServer CreateHost(string address)
{
// Create normal config
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(address);
// Set our own assembly resolver where we add the assemblies we need
CustomAssembliesResolver assemblyResolver = new CustomAssembliesResolver();
config.Services.Replace(typeof(IAssembliesResolver), assemblyResolver);
// Add a route
config.Routes.MapHttpRoute(
name: "default",
routeTemplate: "api/{controller}/{id}",
defaults: new { controller = "Home", id = RouteParameter.Optional });
HttpSelfHostServer server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("Listening on " + address);
return server;
}
static void Main(string[] args)
{
// Create and open our host
HttpSelfHostServer server = CreateHost("http://localhost:8080");
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
}
public class CustomAssembliesResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
ICollection<Assembly> baseAssemblies = base.GetAssemblies();
List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
var controllersAssembly = Assembly.LoadFrom(#"C:\libs\controllers\ControllersLibrary.dll");
baseAssemblies.Add(controllersAssembly);
return assemblies;
}
}

Related

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!

How to pass xml to Asp.net Web Api 2 Get function

Just been stuck here for long time now. Thought someone might be able to put me in right direction. I have created a ASP.NET Web API 2 application. There is one function which will return all ships. Now I want to receive an xml inside my controller function. How can I achieve that. Below is my HttpClient calling code which is giving me Bad Request error. Can someone guide me in right direction to how to pass xml to Web api function.
Routing
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Controller Action
public IHttpActionResult GetSummaryFunction(string id)
{
return Ok(id);
}
Client Code
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:53633/");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/xml"));
string testXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><SummaryRQ xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/Test.WebAPI.Controllers\" Version=\"1.00\" Target=\"Test\"><POS><Source Login=\"test\" Password=\"test\"></Source></POS></SummaryRQ>";
var url = "api/summaryfunction/" + testXml;
HttpResponseMessage response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
}
else
{
}
At minimum you are going to need to pass the testXml string to the Uri.EscapeDataString function.
You may also run into some issues if your Xml is too big. How big is too big depends a lot on the infrastructure that exists between your client and server.

Autofac.Core.DependencyResolutionException doing config.DependencyResolver.GetService(typeof (IUserService)) as IUserService

I need to get a registered instance of type IUserService in my authentication handler.
// Register services
// Build the container.
var containr = builder.Build();
var resolver = new AutofacWebApiDependencyResolver(container);
configuration.DependencyResolver = resolver;
When I run now this line of code:
var userService = configuration.DependencyResolver.GetService(typeof (IUserService)) as IUserService;
I get this exception:
An exception of type 'Autofac.Core.DependencyResolutionException' occurred in Autofac.dll but was not handled in user code
Additional information: No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.
I do NOT want to create the userservice manually because then I have to resolve also manually the depending classes...
How do I get a certain service when I am not inside a request? Dependency.Resolver is unknown/can not be resolved to use that somehow.
UPDATE
I changed my service registration now to:
builder.RegisterType<UserService>()
.As<IUserService>()
.WithParameter(namedParameter)
.InstancePerLifetimeScope();
instead of InstancePerRequestApi()
and resolve the user service like that:
var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
var bla = resolver.GetRootLifetimeScope();
IUserService userService = bla.Resolve<IUserService>(); // Woot!
That works, but what about my former InstancePerApiRequest ? I would like to keep it too!
UPDATE
public static void Register(HttpSelfHostConfiguration config)
{
config.MapHttpAttributeRoutes();
var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
var scope = resolver.GetRootLifetimeScope();
var userService = scope.Resolve<IUserService>();
scope.Dispose();
var authenticationHandler = new AuthenticationHandler(userService);
var tokenHandler = new TokenHandler();
config.MessageHandlers.Add(new HttpsHandler());
config.MessageHandlers.Add(new AllowCommonVerbsHandler());
config.MessageHandlers.Add(authenticationHandler);
config.MessageHandlers.Add(tokenHandler);
config.Routes.MapHttpRoute(
name: "Authentication",
routeTemplate: "api/users/{id}",
defaults: new { controller = "users", id = RouteParameter.Optional },
constraints: null,
handler: authenticationHandler // Put this handler later on the DefaultApi
);
config.Routes.MapHttpRoute(
name: "TokenApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: tokenHandler
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { controller = "Home", action = "Start" }
);
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
}
I assume that your authentication handler is a DelegatingHandler. If this is the case, you can access the lifetime scope for the current request using the following code:
var scope = request.GetDependencyScope();
var requestScope = scope.GetRequestLifetimeScope();
In the code above request is the HttpRequestMessage instance pass to the SendAsync method of the DelegatingHandler.
You will not be able to have the constructor dependency in this case. Inside the SendAsync method use the code I provided above to resolve the IUserService instance when you need to use it.
To create a test for your handler you will need to mock the Web API dependency scope.
var builder = new ContainerBuilder();
// Configure container for test.
var container = builder.Build();
var request = new HttpRequestMessage();
var lifetimeScope = container.BeginLifetimeScope(AutofacWebApiDependencyResolver.ApiRequestTag);
var scope = new AutofacWebApiDependencyScope(lifetimeScope);
request.Properties.Add(HttpPropertyKeys.DependencyScope, scope);
See the HttpMessageInvoker for how to unit test a DelegatingHandler.
http://nerditorium.danielauger.com/blog/2013/02/05/unit-testing-a-webapi-delegatinghandler-with-a-dependencyscope-via-an-httpmessageinvoker/
I think you may have forgotten to add readonly while creating an object of class while using IRepository or you may have forgotten the access modifier before declaring constructor. Hope this might help you.

ASP.NET MVC Routing - differences in web site vs virtual directory

I have an ASP.NET MVC 3 application (in IIS 7.5) with a portable area. When I host the application consuming the portable area in a web site my routing works perfectly e.g.
http://localhost:9001/Clearance/Home/Search (this works)
However, when hosting in a virtual directory e.g.
http://localhost/Acme.Risks.Clearance.Web.Area.TestUI/Clearance/Home/Search (this does not work)
I get the following error:
Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('Clearance/{controller}/{action}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
I don't understand why, I am specifying namespaces it works fine when running in a web site.
Here is my portable area registration:
public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
base.RegisterArea(context, bus);
context.MapRoute(
RouteName.ClearanceAreaDefault,
this.AreaName + "/{controller}/{action}/{id}",
new { controller = "Home", action = "Search", id = UrlParameter.Optional },
new[] { typeof(HomeController).Namespace });
}
Here is the Global.asax for the web application in a virtual directory (does not work):
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { typeof(HomeController).Namespace });
}
Here is the Global.asax for the web application in a web site (does work):
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "Acme.Risks.Web.UI.Controllers" });
}
I have used Phil Haack's "RouteDebugger" (http://nuget.org/packages/routedebugger) and the route debug information on both requests is identical.
Does anyone know why this is happening?
Thanks,
Callum
This was down to the namespace used in the data context for MVC.
My web area assembly is "Acme.Risks.Clearance.Web.Area".
My web site assembly is "Acme.Risks.Web.UI" (working).
My web area test UI assembly is "Acme.Risks.Clearance.Web.Area.TestUI" (not working).
The namespace used in the data context was "Acme.Risks.Clearance.Web.Area*", so in the case of the web area test UI the wildcard caused two matches. So even though the routing information was identical, ASP.NET MVC could not decide which was the right assembly because there were duplicate matches.

Configuring IIS 6.0 to run an MVC3 application

Configuring IIS 6.0 to run an MVC3 application
I think I have a configuration issue on my IIS 6 server and I'd like to see if there's anything I've missed.
The problem that I'm having is that anytime when a RedirectToAction("Index", "Home") is executed (e.g. in a method that returns an ActionResult) I would expect that I would be returned to:
http://servername.domain.com/virtualdirectoryname
However, instead I get redirected to:
http://servername.domain.com/virtualdirectoryname/virtualdirectoryname
That is a second instance of the virtualdirectoryname appended to the end of the URL and can't figure out why - this URL will of course yield a 404 resource not found error. I written and deployed several MVC3 applications both in corporate intranet and public internet environments and can't figure out what I've done wrong. My global.asax.cs seems ok -
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the stored user-data, in this case, our roles
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(id, roles);
}
}
}
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
The Application_AuthenticateRequest handles the storing of the roles for logged on users, but other than that, it seems pretty vanilla. The only think I can think of is that I've somehow messed up the virtual directory.
Prior to performing any of these steps, I had verified that MVC3 and v4.0 of the .NET framework were installed on the server. There are also other ASP.NET 4.0 applications on this server that have been running without incident. There is also an MVC2 application (MVC2 is also installed) running on this server and has been running without incident.
I created a virtual directory off of the main "default site" using the IIS manager.
Setup appropriate permissions on the folder that this virtual directory points to. Tested with a quick "Hello, World" index.html file.
Copied the application from my development PC where the application works as developed to the folder described in #2.
Updated the Web.Config file, editing the connection strings to point to the test database server; I had also verified these connection strings on my development PC.
Open the web browser and hope for the best.
Any assistance is greatly appreciated.
Thanks!
I think what you may be seeing is:
http://servername.domain.com/virtualdirectoryname/applicationname
If you have named your virtual directory the same name as your application then I could see how that could confuse you. If you had no virtual directory and just your application at the root of the Default Web Site you'd be seeing:
http://servername.domain.com/applicationname
Is your virtual directory the same name as your application name? If so, that is why you see this.

Resources