HttpModule doesn't seem to work - asp.net

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?

Related

How can I manage robots.txt in Azure app service with a staging slot

I have an ASP.NET MVC app running in an Azure app service with one staging slot, and a build and release pipeline in VSTS.
I want the production instance to have Allow / in robots.txt and Disallow / in the staging slot at all times.
Currently we are changing robots.txt manually every time we do a swap but this is error prone
How can I automate this process?
To solve this problem I did consider creating the robots.txt file dynamically based on app settings set in the Azure portal (set to stay with the slot), however this won't work since after the swap happens prod will have the staging Disallow rule.
Can anyone advise the best way to manage this?
Robots are mainly used by search engines to crawl and check pages on the public websites. Staging and other deployment slots are not public (and should not be public — unless you have a good reason for that), and thus it doesn't make much sense to configure and manage it. Secondly, in most cases I would recommend to redirect any public request to your production slot and keep staging offline and active for internal use cases only. This would also help you to manage the analytics and logs coming from the public only, and not being polluted with internal and deployment slots stuff.
Anyways, if you are still inclined to do this, then there is one way that you can manage this. Write your own routing to control the robots file, and then render a content-type: text/plain page, which would be dynamic based on whether it is a staging or production request. Something like this,
// Create the robots.txt file dynamically, by controlling the URL handler
[Route("robots.txt")]
public ContentResult DynamicRobotsFile()
{
StringBuilder content = new StringBuilder();
content.AppendLine("user-agent: *");
// Check the condition by URL or Environment variable
if(allow) {
content.AppendLine("Allow: /");
else {
content.AppendLine("Disallow: /");
}
return this.Content(stringBuilder.ToString(), "text/plain", Encoding.UTF8);
}
This way you can manage how the robots.txt is created and you would be able to control the allow disallow for the robots. You can create a separate controller or an action only in the home controller of your app.
Now that you know how to do, you can setup the environment variables for the production/staging slots to check other requirements.
I use below code and It works for me
[Route("robots.txt")]
public ContentResult DynamicRobotsFile()
{
StringBuilder content = new StringBuilder();
if (System.Configuration.ConfigurationManager.AppSettings["production"] != "true")
{
content.AppendLine("user-agent: *");
content.AppendLine("Disallow: /");
}
return this.Content(content.ToString(), "text/plain", Encoding.UTF8);
}
web.config
<appSettings>
<add key="production" value="false" />
</appSettings>
<system.webServer>
<handlers>
<add name="RobotsTxt" path="robots.txt" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
EDITED
I use this version now.
[Route("/robots.txt")]
public ContentResult RobotsTxt()
{
var sb = new StringBuilder().AppendLine("User-Agent: *");
if (_env.IsProduction())
{
sb.AppendLine("Allow: /");
sb.AppendLine("Disallow: /admin");
}
else
{
sb.AppendLine("Disallow: /");
}
sb.AppendLine(string.Empty);
sb.AppendLine($"Sitemap: {this.Request.Scheme}://{this.Request.Host}/sitemap.xml");
return this.Content(sb.ToString(), "text/plain", Encoding.UTF8);
}
and I use IWebHostEnvironment to detect prod or not
public class SeoController : Controller
{
private readonly IWebHostEnvironment _env;
public SeoController(IWebHostEnvironment env)
{
_env = env;
}
}

Hangfire.io Dashboard mapped to IIS Virtual Directory

I'm having trouble getting the Hangfire (1.5.8) dashboard to work inside of an IIS Virtual Directoy. Everything works beautifully in my dev environment where my application is simply mapped to the root of localhost. Our beta server, on the other hand, uses Virtual Directories to separate apps and app pools.
It's an ASP.Net MVC site using Hangfire with an OWIN Startup class. It gets deployed to http://beta-server/app-name/. When I attempt to access either http://beta-server/app-name/hangfire or http//beta-server/hangfire I get a 404 from IIS.
For the purposes of troubleshooting this, my IAuthenticationFilter simply returns true.
Here is my Startup.cs, pretty basic:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
GlobalConfiguration.Configuration
.UseSqlServerStorage(new DetectsEnvironment().GetEnvironment());
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
AuthorizationFilters = new[] {new AuthenticationFilter()}
});
app.UseHangfireServer();
}
}
Does anyone have a working implementation that gets deployed to a Virtual Directory? Are there any OWIN middleware admin/management tools I can use to dig into what URL is getting registered within IIS?
I ended up fixing this simply by adding the HTTPHandler to the section in web.config.
<system.webServer>
<handlers>
<add name="hangfireDashboard" path="hangfire" type="System.Web.DefaultHttpHandler" verb="*" />
</handlers>
</system.webServer>
I had a similar issue in ASP.NET Core 2.0 and it required proper authorization setup (I use a middleware to protect the route, so I did not rely on authorization in my example):
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new [] {new HangfireDashboardAuthorizationFilter()}
});
/// <summary>
/// authorization required when deployed
/// </summary>
public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter
{
///<inheritdoc/>
public bool Authorize(DashboardContext context)
{
// var httpContext = context.GetHttpContext();
// Allow all authenticated users to see the Dashboard (potentially dangerous).
// handled through middleware
return true; // httpContext.User.Identity.IsAuthenticated;
}
}
There is not need to change anything in web.config.
For more information check Hangfire documentation about this topic.
I had the exact same problem. In my case, this was because of bad configuration - the Startup class was not called. So try to add the following to your config file:
<add key="owin:appStartup" value="YourProject.YourNamespace.Startup, YourProject" />
<add key="owin:AutomaticAppStartup" value="true" />
Hope this helps.
Martin

ASP.NET MVC5 Customised Inbound Routing

I'm "playing" around with custom inbound URL routing and have came across a problem.
When I pass my custom route a URL to examine, that ends in *.+, my class is not fired when i submit the request.
An example URL would be "~/old/windows.html"
When I step through this in the debugger, my RouteBase implementation doesn't fire. If i edit the url that i pass to the constructor of my route to try to match against "~/old/windows", my implemetation is fired as expected.
Again, If i change the url ro examine to "~/old/windows." the problem reoccurs.
My Route Implementation is below :-
public class LegacyRoute : RouteBase
{
private string[] _urls;
public LegacyRoute(string[] targetUrls)
{
_urls = targetUrls;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (_urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Legacy");
result.Values.Add("action","GetLegacyURL");
result.Values.Add("legacyURL", requestedURL);
}
return result;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
In the RoutesConfig file I have registered my route like so :-
routes.MapMvcAttributeRoutes();
routes.Add(new LegacyRoute(new[]{"~/articles/windows.html","~/old/.Net_1.0_Class_Library"}));
Can anyone point out why there is a problem?
By default, the .html extension is not handled by .NET, it is handled by IIS directly. You can override by adding the following section in Web.config under <system.webServer> -
<handlers>
<add name="HtmlFileHandler" path="*.html" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
As pointed out here. The above will route EVERY .html file request to .NET, you might want to be more specific by providing a more complete path if you don't want your routing to handle every .html file.
I've found the problem, and I'm sure this will help out a lot of fellow developers.
The problem is with IIS Express that is running via Visual Studio.
There is a module configured in the applicationhost.config called :-
UrlRoutingModule-4.0
This is how it looks in file :-
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
You need to set the preCondition Parameter to "".
To do this :-
Run you app via Visual Studio
Right click on IIS Express in your system tray, select "Show All Applications"
Click on the project you wish to edit, then click the config URL.
Open the file with Visual Studio, Locate the module and ammend.
Hope this helps anyone else, who ran into a similar problem.

How to programmatically add actions to application events?

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.

How to secure access to SWF file using ASP.NET?

We have a swf file that we want to secure and make available only to authorized users.
I embedded the file in an aspx page and that works fine, since ASP.NET handles the aspx page, I can use ASP.NET authorization features and in the web.config restrict the access to roles="AllowedUsers" for example.
However smart users could still get to the file by accessing directly for example www.mysite/flash.swf. We want to make that kind of access secure.
Any help would be greatly appreciated!
Thanks!
Aristos,
You were right. Last afternoon just before I went home I tried creating a custom HTTP handler. And it worked nice. :-) Thanks for answering +1
public class CustomFlashHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (!context.User.Identity.IsAuthenticated)
{
context.Response.Redirect("Default.aspx?ReturnUrl=%2felVideo.aspx");
context.Response.StatusCode = 401;
return;
}
var url = context.Request.CurrentExecutionFilePath;
if (string.IsNullOrEmpty(url)) return;
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("filename={0}", url));
HttpContext.Current.Response.AddHeader("Content-Type", "application/x-shockwave-flash");
HttpContext.Current.Response.WriteFile(url);
HttpContext.Current.Response.End();
}
public bool IsReusable
{
get { return false; }
}
}
Like Aristos said, you have to map ASP.NET to handle .swf files in IIS.
alt text http://www.freeimagehosting.net/uploads/30424ac60a.png
Then add the custom mapping in the application's web.config
<httpHandlers>
<add verb="*" path="*.swf" type="XXXXX.Web.XXXXX.CustomFlashHandler" validate="false" />
</httpHandlers>
1: href=http://www.freeimagehosting.net/>http://www.freeimagehosting.net/uploads/30424ac60a.png
1: a href=http://www.freeimagehosting.net/>http://www.freeimagehosting.net/uploads/30424ac60a.png border=0 alt="Free Image Hosting">
I think that the most easy and fast solution is to Map this extention (.swf) to handle by asp.net.
I do not know if its works, because I do not have done that, but you can give it a try.
One other way is to place this files, somewhere hidden, or with complex name, and use an .ashx file to just read and send them. In the .ashx file you need to set the correct Response.ContentType for the flash, and just read and send the correct file.

Resources