Can .NET intercept and change css files?
Asked Answered
H

7

13

UPDATE 1:

I have now setup IIS6 so .NET can handle calls to .css files. What do I have to do now to get it to change css files based on the referal url? So if the referal url is http://intranet/, it should continue calling the old style sheets. If the referal url is http://intranetv2/, it should call the new style sheet.


ORIGINAL QUESTION:

The background:

I have 2 search engines. 1 is old and the other is brand new (development stage). Search engine 1 is on domain1.com and search engine 2 is on domain2.com. Both domains are on the same server. Both search for webpages on domain1.com based on what the user enters into the search engines. The difference between the 2 search engines is that the new one is faster, produces more accurate results, and has a hugely improved user interface. Both search engines will remain live just so the users can get used to the new one in their own time, instead of just throwing them into the deep end and removing the old search engine altogether.

Anyway, enough of the background, basically, as the searchable pages reside on the old domain name with the old search engine, whereas the new search engine is on the new domain name, ...

The question:

... can I use HttpModule, or another part of .NET, or something from IIS6 even to capture the page links generated by the new search engine, and dynamically change the css file attached to the old searchable pages on the old domain?

The reason:

In effect making it look like a full brand new site, where if the search engine on the old domain is used to access the pages on the old domain, the old stylesheet is used, but if the search engine on the new domain is used to access the searchable files on the old domain name, a new stylesheet should be used to make the old pages look new. As there are lots of searchable pages, in the region of 10,000, editing each and every page to add an if statement to check the referral domain name before adding a style sheet to the pages is not a realistic option.

The environment:

The old search engine, along with the searchable pages on the old domain use .net 1.something, but the new search engine on the new domain name is using .net 3.5, and I am using vb.net for the asp.net pages. The server is a IIS6 server.

Haletky answered 1/6, 2011 at 11:30 Comment(1)
You could simple add it as a new theme and switch themes dynamically based on the HttpRequest.Url in the master's PreInit event or checkout the handler in my answer below.Abrade
A
2
  1. In IIS, Setup the HttpHandler to receive all the file types you want (says you have done this)
  2. user Server.MapPath() on HttpRequest.Url.AbsolutePath to get the physical path
  3. Modify the path according to the domain
  4. Write the file to the response stream.

Here is a handler (simplified) that I use routinely to server alternate files for different domains:

using System;
using System.IO;
using System.Web;
public class MultiDomainFileHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string filePath = GetDomainSpecificFilePath(context.Request.Url.Host,
            context.Server.MapPath(context.Request.Url.AbsolutePath));

        if (File.Exists(filePath))
        {
            switch (Path.GetExtension(filePath).ToLower())
            {
                case ".css": context.Response.ContentType = "text/css"; break;
                case ".jpg":
                case ".jpeg": context.Response.ContentType = "image/jpeg"; break;
                //other types you want to handle
                default: context.Request.ContentType = "application/octet-stream"; break;
            }
            context.Response.WriteFile(filePath); //Write the file to response
        }
        else context.Response.StatusCode = 404;
    }

    private string GetDomainSpecificFilePath(string domain, string originalPath)
    {
        string prefix = "";
        switch (domain.ToLower())
        {
            case "intranetv2": prefix = FILE_PREFIX_INTRANETV2; break;
            case "www.example.com": prefix = FILE_PREFIX_EXAMPLE_DOT_COM; break;
            //other domains you want to handle
        }
        string dir = Path.GetDirectoryName(originalPath);
        string fileName = prefix + Path.GetFileName(originalPath);
        return Path.Combine(dir, fileName);
    }

    const string FILE_PREFIX_INTRANETV2 = "v2.", FILE_PREFIX_EXAMPLE_DOT_COM = "ex.com.";
    public bool IsReusable { get { return false; } }
}

Now, you simple need to have alternate files in the same directories. E.g:

/Images/logo.jpg

/Images/v2.logo.jpg

/Styles/mystyle.css

/Styles/v2.mystyle.css

I hope this helps :)

Abrade answered 28/7, 2011 at 7:3 Comment(5)
But the real issue is determining what file to actually write to the stream. Http_referrer points to the same URL in both casesRadiophone
In that case the Request.UrlReferrer can be used instead of HttpRequest.Url above. The update in the question says "So if the referal url is intranet, it should continue calling the old style sheets. If the referal url is intranetv2, it should call the new style sheet." that means the referrer is not same.Abrade
Or that there's an error in the assumption that OP have about what the referer is when requesting a stylesheet. The referer header is the document the css tag is embedded in not the search page. IE there's no information in the referer that can be used to determine whether or not the page was requested based on clicking a link on the new or the old search pageRadiophone
+1 - Although the OP wrote that each search would have a different referrer, it doesn't seem like that will be correct - beyond the first page. Setting a cookie based on the initial search referrer and using that might work better than checking the referrer each time.Toothy
hopefully the OP will update us about which way worked from him. It is always good to know how a problem was solved.Abrade
I
1

Yes, you should be able to use an HttpModule on your "old" application to intercept the calls to the old CSS. Based on the version of IIS you have on your server, you may need to do some configuration to make sure .NET is handling calls to .css files, otherwise your HttpModule would not be called. See this question for reference.

Once .NET is handling calls to CSS, you can dynamically switch the css dynamically in case the request is for the "old" css file.

Here's an old article (since you are on .NET 1.1) that should point you in the right direction for the implementation and the configuration of IIS: "URL Rewriting in ASP.NET". Basically what you are doing is very similar, since you are "rewriting" a specific URL (the one to your old CSS file) to point to different content.

Incorrigible answered 1/6, 2011 at 12:28 Comment(3)
@oshirowanen: I think in that case you should configure IIS to let .NET handle css files. The link I posted should point you to instructions on how to do that.Incorrigible
On intercepting requests for css files, I think the http_referer header will point to the content page that links/imports it, not the search results page that linked to the content page. So even if search results are on http://intranetv2, Request.ServerVariables["HTTP_REFERER'] during the css file request will be http://intranet and you'll continue to get the old css file.Chronogram
@Chronogram I think you're right. Oshirowanen should probably use something else (like an additional parameter in querystring) to tell calls from the old and from the new search engine apart. The flow would be: the new search engine attachs a "&newsearch=1" (or similar) to the urls of the results on "intranet"; the httpmodule inspects urls and sets, for instance, a session cookie to people searching from the new engine; when the "old" css is called, if the session cookie is set, the new css is served instead.Incorrigible
V
1

I do something somewhat similar for files served up by our content management system. If a http handler is turned on, it inspects the filename and path to see if the user has access to the resource. If the user does, it streams the file, otherwise it returns a 401 not authorized.

I don't see why you couldn't use a handler to jump into the pipeline for the css file, check the host name, and stream out the other css file instead (if applicable). This is straightforward in IIS7 with an integrated pipeline (you didn't specify), but is also possible in IIS6 if you let a css extension be processed by .net.

Let me know if you're interested in this approach and I'll track down some code.

Edit - Here's some code

This is not exactly what you're looking for, but you may be able to get some ideas.

NOTE: This is in IIS7 with an integrated pipeline, so in IIS6 you'll have to make a change so that .css files are handled by the .net process.

Public Class FileManagerFileAuthorization
    Implements IHttpHandler

    Public ReadOnly Property IsReusable As Boolean Implements System.Web.IHttpHandler.IsReusable
        Get
            Return True
        End Get
    End Property

    Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest

        Dim req As HttpRequest = context.Request
        Dim absolutePath As String = req.Path
        Dim fileName As String = Path.GetFileName(absolutePath)
        Dim physicalPathAndFileName As String = HttpContext.Current.Server.MapPath(absolutePath)

        If File.Exists(physicalPathAndFileName) Then

            ' roles that the user is part of. If the user is not authenticated they are part of the public role only
            Dim memberRoles As String()
            If req.IsAuthenticated = False Then
                memberRoles = New String() {ConfigurationManager.AppSettings("PublicRole")}
            Else
                Dim r As New Roles()
                memberRoles = r.GetRolesForUser("")
            End If

            ' check permissions: transmit file or deliver 401
            Dim folderVirtualPath As String = Path.GetDirectoryName(absolutePath).Replace("\"c, "/"c)
            Dim permissions As FileManager.FolderPermissions = FileManager.GetFolderPermissions(folderVirtualPath, memberRoles)
            If permissions And FileManager.FolderPermissions.View Then
                context.Response.ContentType = FileManager.GetContentType(fileName)
                context.Response.AddHeader("Content-Length", New FileInfo(physicalPathAndFileName).Length.ToString())
                context.Response.TransmitFile(physicalPathAndFileName)
            Else
                ' unauthorized
                context.Response.StatusCode = 401
            End If

        Else
            ' file not found
            context.Response.StatusCode = 404
        End If

        context.Response.End()

    End Sub

End Class

And the web.config - and again - this is IIS7 so you'll be using the <httpHandlers/> section under system.web section. I'm looking for any file inside the Userfiles directory, but I think you could point right to a file with this.

<system.webServer>
  <handlers>
    <add name="FileManagerFileAuthorization" path="Userfiles*" verb="GET" type="FileManagerFileAuthorization" resourceType="Unspecified" preCondition="integratedMode" />
  </handlers>
</system.webServer>

Note:

To allow .net to handle non-.net files in IIS, you must allow the .net process to handle the processing of these files. To do this, open IIS manager, navigate to the website, and click properties. Go to the 'home directory' tab and click 'configuration.' Add a wildcard mapping, and choose the .net dll. If you're unsure, copy the link from .ascx below.

Since it's IIS6, you won't be able to use the system.webServer section above, you'll need to add http handlers the old way. This link explains it: http://msdn.microsoft.com/en-us/library/46c5ddfy.aspx

Valery answered 1/6, 2011 at 12:29 Comment(5)
I am using IIS6. Any code, guides, tutorials would be great!Haletky
I see a lot of responses are indicating a httpmodule. I think a handler is a better approach, since we're dealing with an actual file, not a request. Here's a good overview: msdn.microsoft.com/en-us/library/bb398986.aspxValery
Using an httphandler (if I'm not misunderstanding your suggestion) would mean changing all the 10000+ pages in the old domain so that instead of referencing the css file, they reference the handler, instead (which in turn should serve the correct css file). I think that's not what @Haletky wanted to doIncorrigible
HTTP Handler sounds good, but if it means having to update all 10,000+ pages on the old domain as ScottE has just mentioned, then I will not be able to consider this as an option. However, if it can be done without updating all the old pages, then please do let me know.Haletky
I'm suggesting that you listen for the css file being served up, and if the domain doesn't match, then server up the other instead. So, in my example instead of issuing a 401 if some condition doesn't match, you'd transmit the different css file instead.Valery
D
1

Based on the domain name, you can dynamically control the part of the page from the code behind (.vb or .cs files or even a class). This will give you control to replace the css file based on the domian name. Capture the domain name in the code and then replace the css file/link, infact the whole part in the code behind. you can do this in C# or in VB.

Decorative answered 8/6, 2011 at 14:35 Comment(0)
K
1

I wouldn't recommend using httpModule as it will be called upon for each and every request which might deteriorate performance. Whereas, you can use httpHandlers to handle only specific paths. Therefore, my vote would be to use httpHandlers.

But there is a glitch. By default, IIS 6 does not pass requests for non ASP.Net extension (read extensions other than .aspx, .ashx, .axd and all that) to ASP.Net by default.

Therefore, you need to add ISAPI module for CSS extension to pass the request to aspnet_isapi.dll (you can find the complete path from .aspx extension handler).

This link might help in setting up ISAPI module.

Once ASP.Net has started handling .CSS extension, write a httpHandler with your logic and add the following line under httphandlers section in web.config file

Say your httpHandler is CSSHttpHandler then the code would be something like this.

<add verb="HEAD,GET" path="*.css" type="CSSHttpHandler">

Hope this helps.

Kathline answered 21/7, 2011 at 8:58 Comment(0)
C
0

Maybe you can add a HTTP module on old domain and check if the request.UrlReferrer is search results page from new domain and then replace links to old style-sheet in generated output.

Cuthburt answered 1/6, 2011 at 12:26 Comment(1)
Would you know of a step by step guide, tutorial I could follow to get this done?Haletky
C
0

As I stated in my comment re: one of the answers, I think the http_referer header will point to the content page that links/imports the requested css file, not the search results page that linked to the content page. So even if search results are on http://intranetv2, Request.ServerVariables["HTTP_REFERER'] during the css file request will be http://intranet and you'll continue to get the old css file.

Seems like you'll have to figure out a way to either serve up the 10000 content pages from the new http://intranetv2 domain, or come up with a way to set a flag during the content aspx page request (maybe in global.asax Application_BeginRequest) that can be read and acted upon during the css file request (by an HttpHandler, like others have suggested).

Not sure what the appropriate signaling mechanism would be though. It needs to work per-user, not per-application as well as be available during and persist across multiple disparate file requests, and the choices are constrained by the use of .NET 1.1.

Chronogram answered 31/7, 2011 at 10:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.