In global.asax we have the possibility to implement methods that corresponds to an application event, for example Application_EndRequest, and add whatever code we want to these.
I'm developing an plugin that have the need to attach to some of these events, is there any way to programmatically push actions for these into the application flow somehow?
The goal is obviously to avoid the need for manually adding code in global.asax when using the plugin.
How to hook an HTTP Module into an MVC application
If I understand you correctly, you want to create an external library and "hook" it up to an MVC application's events.
1. Create a simple class library.
The first thing is to create a simple class library. We'll call it TestLib.
2. Create a new class called TestLibHttpModule.
The class implements IHttpModule. This grants it the Init() method. This method will be called when the module is initialised and it passes us the HttpApplication object that is initialising the module.
In our Init method, we'll attach a new EventHandler to the EndRequest event.
For now, our event handler method will simply throw an exception with a cheeky message.
namespace TestLib
{
public class TestLibHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(context_EndRequest);
}
private void context_EndRequest(object sender, EventArgs e)
{
// get the HttpApplication context
HttpApplication context = (HttpApplication)sender;
throw new NotImplementedException("At least it works.");
}
}
}
3. Point the library's build path to our MVC project.
Assuming you have an MVC project already set up nearby, perhaps called TestApp, point the build path of your library to be the bin folder of your MVC project. Now, every time we build the module, it'll be thrown into the MVC project.
4. Update MVC project to use the HttpModule.
The Web.config of an MVC application has a spot for specifying Http Modules. Under the system.Web element, add a new httpModules section (if it doesn't already exist).
<system.web>
[ ... ]
<httpModules>
<add name="TestLibModule" type="TestLib.TestLibHttpModule, TestLib" />
</httpModules>
</system.web>
5. Run the MVC application.
Related
In Visual Studio 2015 (Enterprise), is there still no built-in tool that will dissect and display the routing information for WebAPI calls?
WebApi Route Debugger does not seem to work for ASP.NET 5 (and mangles the default Help page in the template)
Glimpse does not offer the "Launch Now!" button anymore from what I can tell (http://blog.markvincze.com/use-glimpse-with-asp-net-web-api/).
RouteDebugger is good for figuring out which routes will/will not be hit.
http://nuget.org/packages/routedebugger but you are saying it doesn't work. After some googling I found another solution to your problem,
Add an event handler in Global.asax.cs to pick up the incoming request and then look at the route values in the VS debugger. Override the Init method as follows:
public override void Init()
{
base.Init();
this.AcquireRequestState += showRouteValues;
}
...
protected void showRouteValues(object sender, EventArgs e)
{
var context = HttpContext.Current;
if (context == null)
return;
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
}
Then set a breakpoint in showRouteValues and look at the contents of routeData.
Keep in mind that in a Web Api project, the Http routes are in WebApiConfig.cs ... not RouteConfig.cs
but that's not a tool. may be digging up some thread would help you resolve your issue.
Reference: Is there a way I can debug a route in ASP. MVC5?
We're having an odd issue with a WebAPI application hosted by another ASP.NET webapp. The WebAPI controllers are all mapped with Ninject but the ASP.NET host site does not use Ninject.
The issue is that any requests to any of the WebAPI controllers fail with a Ninject error and HTTP 500:
"An error occurred when trying to create a controller of type 'MyObjectsController'. Make sure that the controller has a parameterless public constructor."
However, once even a single request to the main webapp is made (such as opening the login page) then the WebAPI calls all work as expected. The WebAPI is registered and initialized as part of the Application_Start global event. The start event is triggered regardless of whether the first request comes in under the WebAPI or the webapp so it's not bypassing the global startup when coming through the WebAPI before the main app. The WebAPI registration is pretty standard stuff:
GlobalConfiguration.Configure(AddressOf WebApiConfig.Register)
And the Register function itself is nothing unusual:
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*", "X-Pagination");
//To allow cross-origin credentials in Web API
cors.SupportsCredentials = true;
config.EnableCors(cors);
// To disable host-level authentication inside the Web API pipeline and "un-authenticates" the request.
config.SuppressHostPrincipal();
config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthBearerOptions.AuthenticationType));
// Web API routes
var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));
//constraintResolver.ConstraintMap.Add("NonEmptyFolderIds", typeof(NonEmptyFolderIdsConstraint));
config.MapHttpAttributeRoutes(constraintResolver);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
The NinjectConfig is also pretty standard:
public static class NinjectConfig
{
/// <summary>
/// THe kernel of Ninject
/// </summary>
public static Lazy<IKernel> CreateKernel = new Lazy<IKernel>(() =>
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
RegisterServices(kernel);
return kernel;
});
private static void RegisterServices(KernelBase kernel)
{
kernel.Bind<IMyObjectRepository>().To<MyObjectRepository>().InRequestScope();
...
}
}
An example of the DI usage (again, very basic and standard) is:
public class MyObjectRepository : IMyObjectRepository
{
private readonly IMyOtherObjectRepository _objectRepository;
...
public MyObjectRepository(IMyOtherObjectRepository objectRepository)
{
_objectRepository = objectRepository;
...
}
...
}
We want to avoid the requirement of the initial request to the webapp before the WebAPI is available for requests but nothing seems to be getting us towards a solution.
We initially tried out the IIS preloading/app initialization by setting Start Mode to AlwaysRunning and Start automatically to True in the AppPool config. We also enabled preloadEnabled to true and then added the applicationInitialization config section to the web.config such as the following:
<system.webServer>
...
<applicationInitialization>
<add initializationPage="login.aspx" />
</applicationInitialization>
...
</system.webServer>
However, none of these changes and variations of made any difference to the behavior of the WebAPI. We've scoured the web for more help but are at somewhat of a loss as pretty much everything we've come across points to setting the Start Mode, Start Automatically, preloadEnabled, and applicationInitialization and then it will magically work but that's definitely not our experience.
Does anyone have suggestions or ideas?
Install Ninject integration for WebApi nuget package. It creates a file which initializes Ninject on startup. Here is the doc.
How do I access the current ServletContext from within my Vaadin 7 app?
I want to use the ServletContext object’s setAttribute, getAttribute, removeAttribute, and getAttributeNames methods to manage some global state for my Vaadin app.
Also, if using those methods for that purpose is inappropriate for Vaadin apps, please explain.
tl;dr
For Vaadin 7 & 8, as well as Vaadin Flow (versions 10+):
VaadinServlet.getCurrent().getServletContext()
VaadinServlet
The VaadinServlet class inherits a getServletContext method.
To get the VaadinServlet object, call the static class method getCurrent.
From most anywhere within your Vaadin app, do something like this:
ServletContext servletContext = VaadinServlet.getCurrent().getServletContext();
CAVEATDoes not work in background threads. In threads you launch, this command returns NULL. As documented:
In other cases, (e.g. from background threads started in some other way), the current servlet is not automatically defined.
#WebListener (ServletContextListener)
By the way, you are likely to want to handle such global state when the web app deploys (launches) in the container.
You can hook into your Vaadin web app’s deployment with the #WebListener annotation on your class implementing the ServletContextListener interface. Both methods of that interface, contextInitialized and contextDestroyed, are passed a ServletContextEvent from which you can access the ServletContext object by calling getServletContext.
#WebListener ( "Context listener for doing something or other." )
public class MyContextListener implements ServletContextListener
{
// Vaadin app deploying/launching.
#Override
public void contextInitialized ( ServletContextEvent contextEvent )
{
ServletContext context = contextEvent.getServletContext();
context.setAttribute( … ) ;
// …
}
// Vaadin app un-deploying/shutting down.
#Override
public void contextDestroyed ( ServletContextEvent contextEvent )
{
ServletContext context = contextEvent.getServletContext();
// …
}
}
This hook is called as part of your Vaadin app being initialized, before executing the Vaadin servlet (or any other servlet/filter in your web app). To quote the doc on the contextInitialized method:
Receives notification that the web application initialization process is starting.
All ServletContextListeners are notified of context initialization before any filters or servlets in the web application are initialized.
All, I'm trying to implement an HttpModule (IHttpModule) to catch pages request and redirect to a new page. Unfortunately, it seems I can't use the Session in the new page. because the Session is null.
Here is my code looks like. please review it .
public class MyModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
....
HttpContext.Current.Server.Transfer("newpage.aspx");//redirect to new page.
}
}
In the newpage.aspx, There is an exception says Object reference not set to an instance of an object for the code HttpContext.Current.Session[xxx], Because the HttpContext.Current.Session is null .
Could someone tell me what happen to it ?
Thanks.
Update
All, I found If I use the HttpContext.Current.Response.Redirect to redirect url . Everything is ok. I mean the Session object is initiated before being used.But that doesn't work for Server.Transfer.
I already knew what is the difference of these two.
The normal aspx runtime pipeline with 2 modules is:
--> HttpModule_1.BeginRequest(); --> HttpModule_2.BeginRequest(); --> HttpHandler(Page)
<-- HttpModule_1.EndRequest(); <-- HttpModule_2.EndRequest(); <-- HttpHandler(Page)
Imagine HttpModule_1 as your custom module and HttpModule_2 as the aspx session module.
Your custom module is running first, so no session is filled in when your module is running.
When HttpModule_1 is running BeginRequest you add a Server.Trasfer(). Now, the server transfer will execute the HttpHandler of the request page immediately, it will be called without leaving the BeginRequest and after the handler is done the transfer method will run the Request.End() and terminate the processing.
So, the handler for the "newpage.aspx" will also run before the session module. (actually, the session module will never run because Server.Transfer() will end the request).
If you guaranty that the session module is processes before your module you should resolve the problem (see https://stackoverflow.com/a/2427632/953144).
I've created a simple HttpModule to log the uses of my existing webservice. There's a dll containing a single class
public class TrackingModule : System.Web.IHttpModule
{
public TrackingModule(){}
public void Init(System.Web.HttpApplication context)
{
context.BeginRequest+=new EventHandler(context_BeginRequest);
}
public void Dispose()
{
}
private void context_BeginRequest(object sender, EventArgs e)
{
try
{
Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManager.Publish( new Exception("Log attept") );
HttpApplication app = (HttpApplication)sender;
string method = app.Request.RawUrl;
SaveUseToDatabase( app.Request.UserHostAddress, method );
}
catch( Exception ex )
{
try
{
Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManager.Publish( ex );
}
catch{}
}
}
}
After compiling the dll I add it to webservice's bin folder and in webservice's web.config I add:
<system.web>
<httpModules>
<add name="TrackingModule" type="WebserviceTrackingModule.TrackingModule, WebserviceTrackingModule"/>
This works fine on my computer, but when I copy it to production server, nothing happens. No new entries in database, no entries logged by ExceptionManager. As if it's not there at all.
What can I be missing?
Edit:
After performing another test I can add that it works when I add it for a webservice that has it's own top-level virtual directory. It doesn't work for webservices that reside in virtual directories that are subfolders of another virtual directory.
I know that HttpModules settings are being inherited by subdirectories, but it looks like the existence of parent directory gets in the way.
I believe I have found a better solution. Attach the module at runtime instead of in the web config. Check out Rick Strahl's blog post for the details.
OK, I'm answering my own question.
It doesn't work when you define <httpModules> in subdirectory's web.config, even when the subdirectory is configured as an application. The only solution I found so far is to define them within <location> tag in web.config of root application (parent directory).
I don't like it :(
I found the answer to this question in http://forums.iis.net/t/1151924.aspx
oh well, a little
process-of-elimination never fails
me.
After staring at the 3.5-related
web.config code, I realized that my
module needed to be added to the new
section:
<system.webserver>
<modules>
instead of system.web...at least it's
working now.
So to translate that:
If you are having a problem with httphandlers
add your handler to the modules node in system.webserver and see if that works
Copy the format used for scriptmodule.
Does this work?
<add name="TrackingModule" type="WebserviceTrackingModule.TrackingModule" />
And is the context_BeginRequest method definitely being called for each request?