I'm developing a plugin like application for a web site. Due to requirements it will live in the same physical path and the same application pool as the main web site which I do not have the source code for. So they both share the same Web.config and some other dependencies.
I need to set a license for a third party dependency at application start, but I have no way of accessing the code behind for Global.asax as that code is owned by a different company.
So, is there an alternate way of appending events to Application Start without involving Global.asax or is my only solution to inherit/extend the current Global.asax?
You can use a HTTPModule:
public class MyModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
...
}
#endregion
}
web.config
<httpModules>
<add name="MyModule" type="MyNamespace.MyModule, MyAssembly" />
</httpModules>
You can use WebActivator if you know your target projects will all be .NET 4.0 or greater.
https://github.com/davidebbo/WebActivator
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(Bootstrapper), "Start")]
public class Bootstrapper
{
public static void Start()
{
// Put everything in motion here
}
}
Note you also have the option of running before or after the Global.asax Application_Start() function - this example runs afterward.
Related
I am unable to run the chat application using singalr and I am facing problem with owin.
The following errors occurred while attempting to load the app:
No assembly found containing an OwinStartupAttribute.
No 'Configuration' method was found in class 'Microsoft.VisualStudio.Web.PageInspector.Runtime.Startup, Microsoft.VisualStudio.Web.PageInspector.Runtime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
To disable OWIN startup discovery, add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
To specify the OWIN startup Assembly, Class, or Method, add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.
Sounds like you're mixing 1.0 and 2.0 components in your project. See the following tutorial for how to upgrade a 1.0 project to 2.0:
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/upgrading-signalr-1x-projects-to-20
Do you have the Startup.cs in your project? More code please.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
In addition have you setup any hubs?
public class ChatBoxHub : Hub
{
public ChatBoxHub()
{
}
}
Is there a way to write some code that would be executed for each request to a .aspx or a .cshtml page in asp.net 4.5 apart from using a base page class. it is a very huge project and making changes to all pages to use a base page is a nightmare. Also i am not sure how would this be done for a cshtml page since they don't have a class.
Can we use the Application_BeginRequest and target only the aspx and cshtml files since the website is running in integrated mode.?
basically, i have to check if a user who is accessing the website has a specific ip address against a database and if yes then allow access otherwise redirect.
we are using IIS8 and ASP.Net 4.5 and ASP.Net Razor Web Pages
Also i am not sure how would this be done for a cshtml page since they don't have a class.
You could place a _ViewStart.cshtml file whose contents will get executed on each request.
Alternatively you could write a custom Http Module:
public class MyModule: IHttpModule
{
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(OnBeginRequest);
}
public void Dispose()
{
}
public void OnBeginRequest(object s, EventArgs e)
{
// this code here's gonna get executed on each request
}
}
and then simply register this module in your web.config:
<system.webServer>
<modules>
<add name="MyModule" type="SomeNamespace.MyModule, SomeAssembly" />
</modules>
...
</system.webServer>
or if you are running in Classic Mode:
<system.web>
<httpModules>
<add name="MyModule" type="SomeNamespace.MyModule, SomeAssembly" />
</httpModules>
</system.web>
basically, i have to check if a user who is accessing the website has
a specific ip address against a database and if yes then allow access
otherwise redirect.
Inside the OnBeginRequest method you could get the current user IP:
public void OnBeginRequest(object sender, EventArgs e)
{
var app = sender as HttpApplication;
var request = app.Context.Request;
string ip = request.UserHostAddress;
// do your checks against the database
}
Asp.net MVC filters are especially designed for that purpose.
You would implement ActionFilterAttribute like this (maybe put this new class in a Filters folder in your webapp solution):
public class IpFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string ip = filterContext.HttpContext.Request.UserHostAddress;
if(!testIp(ip))
{
if (true /* You want to use a route name*/)
filterContext.Result = new RedirectToRouteResult("badIpRouteName");
else /* you want an url */
filterContext.Result = new RedirectResult("~/badIpController/badIpAction");
}
base.OnActionExecuting(filterContext);
}
private bool testIp(string inputIp)
{
return true /* do you ip test here */;
}
}
Then you have to decorate any action that would perform the ipcheck with IpFilter like so :
[IpFilter]
public ActionResult AnyActionWhichNeedsGoodIp()
{
/* do stuff */
}
My project has no "global.asax" for various reasons and I can't change that (it's a component). Also, I have no access to web.config, so an httpModule is also not an option.
Is there a way to handle application-wide events, like "BeginRequest" in this case?
I tried this and it didn't work, can someone explain why? Seems like a bug:
HttpContext.Current.ApplicationInstance.BeginRequest += MyStaticMethod;
No, this is not a bug. Event handlers can only be bound to HttpApplication events during IHttpModule initialization and you're trying to add it somewhere in the Page_Init(my assumption).
So you need to register a http module with desired event handlers dynamically. If you're under .NET 4 there is a good news for you - there is PreApplicationStartMethodAttribute attribute (a reference: Three Hidden Extensibility Gems in ASP.NET 4):
This new attribute allows you to have
code run way early in the ASP.NET
pipeline as an application starts up.
I mean way early, even before
Application_Start.
So the things left are pretty simple: you need to create your own http module with event handlers you want, module initializer and attribute to your AssemblyInfo.cs file . Here is a module example:
public class MyModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
public void Dispose()
{
}
void context_BeginRequest(object sender, EventArgs e)
{
}
}
To register module dynamically you could use DynamicModuleUtility.RegisterModule method from the Microsoft.Web.Infrastructure.dll assembly:
public class Initializer
{
public static void Initialize()
{
DynamicModuleUtility.RegisterModule(typeof(MyModule));
}
}
the only thing left is to add the necessary attribute to your AssemblyInfo.cs:
[assembly: PreApplicationStartMethod(typeof(Initializer), "Initialize")]
I'm trying to get Session enabled in the GettHttpHandler method of my IRouteHandler classes but session is always null. Could someone tell me what I'm doing wrong?
In global.asax I have
RouteTable.Routes.Add("All", new Route("{*page}", new MyRouteHandler()));
The MyRouteHandler class where Session is null looks like this:
public class MyRouteHandler : IRouteHandler, IRequiresSessionState
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string test = HttpContext.Current.Session["test"].ToString();
return BuildManager.CreateInstanceFromVirtualPath("~/Page.aspx", typeof(Page)) as Page;
}
}
I made a small test app that shows the problem.
Could someone tell me what I'm doing wrong?
Edited to add:
Yes, I really need session data in the route handler. There are many reasons but one easily explainable is when the user can switch to browse the site in preview mode.
The site consists of a hierarchy of dynamic pages (/page1/page2...) in the database that can be published normally or to preview. A content producer browsing the site can choose to view just normal pages or also those published to preview. The browsing mode is stored in the user's session so therefor the route handler needs to know the browsing mode to be able to solve the requested page.
So I really need the session already at that stage.
I have explained reason behind this problem in this answer. And now I have found a solution to the problem!
You create a custom HttpHandler class:
class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
public MyRequestHandler RequestHandler;
public RequestContext Context;
public MyHttpHandler(MyRequestHandler routeHandler, RequestContext context)
{
RequestHandler = routeHandler;
Context = context;
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
public bool IsReusable
{
get { throw new NotImplementedException(); }
}
}
It is important to add IRequiresSessionState interface, otherwise IIS does not load session for this request. We do not need to implement logic of ProcessRequest and IsReusable, but class must implement the IHttpHandler interface.
You change your RouteHandler implementation:
public class MyRequestHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MyHttpHandler(this, requestContext);
}
public IHttpHandler DelayedGetHttpHandler(RequestContext requestContext)
{
// your custom routing logic comes here...
}
}
You simply move your original, Session dependent logic to DelayedGetHttpHandler function and in the GetHttphandler function you return an instance of the helping MyHttpHandler class.
Then, you hook your handling logic to the HttpApplication.PostAcquireRequestState event, e.g. in the Global.asax:
public class Global : HttpApplication
{
public override void Init()
{
base.Init();
PostAcquireRequestState += Global_PostAcquireRequestState;
}
}
For more reference, check this page: https://msdn.microsoft.com/en-us/library/bb470252(v=vs.140).aspx. It explains the request lifecycle and why I use the PostAcquireRequestState event.
In the event handler, you invoke your custom RouteHandling function:
void Global_PostAcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Handler is MyHttpHandler) {
var handler = HttpContext.Current.Handler as MyHttpHandler;
HttpContext.Current.Handler = handler.RouteHandler.DelayedGetHttpHandler(handler.Context);
}
}
And that's it. Works for me.
I am not sure that you can do this (although I may be wrong). My recollection is that IRequiresSessionState indicates that an HttpHandler needs session state, rather than the route handler (which is responsible for giving you a handler appropriate to the route).
Do you really need the session in the route handler itself and not the handler it gives out?
Well I know this is old thread but just putting up the answer here if anyone like me falls in the same scenario, I found an answer here
What you do is just add a runAllManagedModulesForAllRequests="true" attribute to your modules tag in web.config like below
<system.webServer>
.....
<modules runAllManagedModulesForAllRequests="true">
........
</modules>
......
</system.webServer>
However this is not a good solution as it calls managed module everytime, i am using
<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
add it in modules section of web.config, this a better solution than the previous one.
I'm writing an app where 3rd party vendors can write plugin DLLs and drop them into the web app's bin directory. I want the ability for these plugins to be able to register their own HttpModules if necessary.
Is there anyway that I can add or remove HttpModules from and to the pipeline at runtime without having a corresponding entry in the Web.Config, or do I have to programmatically edit the Web.Config when adding / removing modules? I know that either way is going to cause an AppDomain restart but I'd rather be able to do it in code than having to fudge the web.config to achieve the same effect.
It has to be done at just the right
time in the HttpApplication life cycle
which is when the HttpApplication
object initializes (multiple times,
once for each instance of
HttpApplication). The only method
where this works correct is
HttpApplication Init().
To hook up a module via code you can
run code like the following instead of
the HttpModule definition in
web.config:
public class Global : System.Web.HttpApplication
{
// some modules use explicit interface implementation
// by declaring this static member as the IHttpModule interface
// we work around that
public static IHttpModule Module = new xrnsToashxMappingModule();
public override void Init()
{
base.Init();
Module.Init(this);
}
}
All you do is override the HttpApplication's Init() method and
then access the static instance's Init
method. Init() of the module hooks up
the event and off you go.
Via Rick Strahl's blog
Realize this is an old question, but asp.net 4 provides some new capabilities that can help here.
Specifically, ASP.NET 4 provides a PreApplicationStartMethod capability that can be used to add HttpModules programmatically.
I just did a blog post on that at http://www.nikhilk.net/Config-Free-HttpModule-Registration.aspx.
The basic idea is you create a derived HttpApplication that provides ability to add HttpModules dynamically at startup time, and it then initializes them into the pipeline whenever each HttpApplication instance is created within the app-domain.
The dll Microsoft.Web.Infrastructure.dll has a method for this inside the class DynamicModuleUtility.
The dll is shipped with WebPages 1.0
public static class PreApplicationStartCode
{
private static bool _startWasCalled;
public static void Start()
{
if (_startWasCalled) return;
_startWasCalled = true;
DynamicModuleUtility.RegisterModule(typeof(EventTriggeringHttpModule));
}
}
This worked for me for dynamic registration.
RegisterModule(typeof(RequestLoggerModule));
public class RequestLoggerModule : IHttpModule
{ ... }
https://learn.microsoft.com/en-us/dotnet/api/system.web.httpapplication.registermodule?view=netframework-4.7.2
In new versions of ASP MVC you can use Package Manager to add a reference to WebActivatorX and then do something like this
using WhateverNameSpacesYouNeed;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(YourApp.SomeNameSpace.YourClass), "Initialize")]
namespace YourApp.SomeNameSpace
{
public static void Initialize()
{
DynamicModuleUtility.RegisterModule( ... the type that implements IHttpModule ... );
}
}