enable ashx http handler on theme

Topics: Customizing Orchard, Writing themes
Jun 7, 2011 at 5:19 PM

Hi.

I'm deploying a theme with a bit of custom code.

I already managed to have a Filters\Layout.cs file that enables different layout.cshtml files for homepage, and other pages, by adding the compile tag in a shipped .csproj file within the nugget.

Now I need to add a handler.ashx and e handler.dll. The handler opens up resources that are inside the assembly, and are called through css as image urls.

e.g. .mystyle { background:white url(../Content/images/handler.ashx?id=abc); }

So after installing my theme, I need the http module working but I get 404 not found when the ashx file is called. The path is correct so I guess the ashx extension is being blocked or needs some explicit changes in the web.config file.

Can someone tell me what I need to do to my nugget so when installing, the ashx can be called right away? I added a compile line for the ashx too, but that didn't make it work.

Regards,

Tiago.

Coordinator
Jun 7, 2011 at 5:26 PM

This ashx should not be in /Content, because some web.config configuration will prevent it. Place it in another root folder, and it should work.

Jun 7, 2011 at 5:56 PM
sebastienros wrote:

This ashx should not be in /Content, because some web.config configuration will prevent it. Place it in another root folder, and it should work. 

ok I placed in my /Filters folder which has no web.config

now I don't get the blueish iis error page, but the white error page, still with 404. are you sure it will work without web.config file? the ashx is to be called through the browser and not having a web.config won't prevent the ashx from being accessible?

 

Server Error in '/' Application.

The resource cannot be found.

Jun 7, 2011 at 6:12 PM

Why not implement a Controller and Action instead of the HttpHandler? It'll be much easier, and neatly packaged up as a module.

Jun 7, 2011 at 6:13 PM
Edited Jun 7, 2011 at 6:13 PM

BTW; sebastien's Media Folder module implements an image handler in a controller already if you want to see an example of how it's done.

Jun 9, 2011 at 9:19 AM
randompete wrote:

BTW; sebastien's Media Folder module implements an image handler in a controller already if you want to see an example of how it's done.

I'll look into it.

Still, I didn't want it to get too complex. My theme package already ships the dll and ashx. All I need is for the css to be able to call the ashx but accessing the file as is throws 404. I thought there was something simple we could add to the web.config that allowed access to the file by any resources.

Thanks.

Jun 9, 2011 at 10:57 AM

Controllers are even simpler than ashx services :)

But, like sebastien said, the ashx should work from a root folder, not from Filters or any other subfolder.

Jun 15, 2011 at 3:26 PM

Server Error in '/' Application.


The resource cannot be found.

Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.

Requested URL: /Themes/testeorxinvent/Imfuscator.ashx


Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1

***

this is what I get if I place the ashx in the theme's root folder.

this error appears even if the ashx file is completely blank

Jun 15, 2011 at 3:30 PM

It will have to be the website root, not in any subfolder of the website.

Jun 15, 2011 at 4:52 PM

I placed in the root folder of the installation

still the same error

 

back in the content/images folder, direct access to the file throws

HTTP Error 404.3 - Not Found

The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map.

Coordinator
Jun 15, 2011 at 5:52 PM

It's probably not enabled in web.config. Our web.config is pretty much locked down.

Jun 16, 2011 at 9:57 AM

any idea how to enable the ashx file in web.config?

Jun 16, 2011 at 10:59 AM

Since you can't change Orchard's Web.config when you install a module, I'd strongly suggest you look at writing a controller. They are honestly really, really simple!

You could also look at service routes, which I think would let you route the service class without needing the ashx.

Jun 16, 2011 at 4:12 PM

before I try anything else, I wanted to find out if there is a way of activating ashx files.

themes have a folder-specific web.config in /Content/Images and /js to enable image and javascript file extensions to be enabled within the browser.

all I want is for another extension to be enabled in that web.config so that the page doesn't throw 404 on that file

I understand this could be done some other way, but I'm being given this output to work with and I really need to try and work with what I have (ashx+dll). there is other stuff that depend on the ashx file

Jun 16, 2011 at 5:57 PM

The thing is, ashx files requires various thing to be happening in the HTTP pipeline. The Web.configs you're talking about are simply enabling static files to be served from a particular folder; very different to using a web service. I'm wondering what could possibly depend on the ashx file that couldn't also be achieved by a controller? From the point of view of the service consumer, it's just a URL that returns some data - it doesn't care whether it's an ashx or a controller delivering that data behind the scenes.

Either way; have you looking into service routes like I said? I think they're the proper MVC way of routing services.

Coordinator
Jun 16, 2011 at 8:10 PM

Just did a quick experiment to enabled a ashx handler in an Orchard module. Good news: it can work. Bad news: it requires a bug fix in the Orchard Framework.

Here is what I did:

1. Create a "WebHandlers" folder in your module's folder

2. Add the following web.config in the "WebHandlers" folder:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <httpHandlers>
      <add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="true" />
    </httpHandlers>
    </system.web>
</configuration>

3. Drop your ashx file in the "WebHandlers" folder.

4. Access your handler from the url corresponding to the ashx file location (e.g. http://localhost/MyModule/WebHandlers/MyHandler.ashx"

At this point you should get an error about the "MyHandler" type cannot be created. The reason is the ASP.NET doesn't know which assembly to look for your handler C# class.

One option is to copy your module to the Orcard.web/bin directory, but that won't work for a module you share (not an Orchard built-in deployment option).

The other option is to make a slight change in the Orchard.Framework code. In the "Orchard.FileSystems.Dependencies.WebFormVirtualPathProvider" class, there is this line of code:

        private readonly string[] _extensions = { ".ascx", ".aspx", ".master" };

 If you change it to this:

        private readonly string[] _extensions = { ".ascx", ".aspx", ".ashx", ".master" };

 Your handler should be activated as expected.

Feel free to open a bug if you think this scenario is important and using a controller doesn't meet your needs.

 HTH,

Renaud

Jun 17, 2011 at 3:59 PM
Edited Jun 17, 2011 at 4:01 PM

thanks for taking the time.

again, I understand that this could be done another way, and probably a better way, but the ashx files are being generated by another tool and I'm adding it to theme packages. having this work another way would mean having an orchard-specific controller

I've tried that on a theme (not a module) and I still get server error, "the resource cannot be found".

I've also tried creating the nugget previously with the WebHandlers folder and contents and then uninstall/install the theme, but any calls to ashx files anywhere either throw 404 (root folders, folders with no web.config, web.config from rpaquay) or 404.3 (sub folders with "default" web.config below). I even tried a mix of the two web.configs!

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
  <system.web>
    <httpHandlers>
      <!-- iis6 - for any request in this location, return via managed static file handler -->
      <add path="*" verb="*" type="System.Web.StaticFileHandler" />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <handlers accessPolicy="Script,Read">
      <!--
      iis7 - for any request to a file exists on disk, return it via native http module.
      accessPolicy 'Script' is to allow for a managed 404 page.
      -->
      <add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
    </handlers>
  </system.webServer>
</configuration>

Jun 17, 2011 at 5:24 PM

Again: have you looked at MVC Service Routes?

One way or another you'll need Orchard-specific code of some form, but maybe that could be as simple as defining a service route to your IHttpHandler.

Jun 17, 2011 at 5:46 PM
randompete wrote:

Again: have you looked at MVC Service Routes?

One way or another you'll need Orchard-specific code of some form, but maybe that could be as simple as defining a service route to your IHttpHandler.

 

right now I just want a way to get this working, I don't care how :-p

can you provide a step-by-step walkthrough on how to create a very simple service/controller that would enable my ashx, or replace what it does?

 

thanks

Jun 17, 2011 at 6:48 PM

http://stackoverflow.com/questions/186062/can-an-asp-net-mvc-controller-return-an-image

The File() method can also accept a byte array, which you can presumably get from the DLL that your ashx was calling.

Jun 20, 2011 at 11:56 AM
Edited Jun 20, 2011 at 11:57 AM

ok I'm trying to work this out and a little guidance would be appreciated

 

I'm creating a .cs file and placing it into the Fitlers folder which i already had for the LayoutFilter.cs from bleroy

In my .csproj file I have

    <Compile Include="Filters\LayoutFilter.cs" />
    <Compile Include="Filters\Imfuscator.cs" />

So when I upload my theme, both cs files are compiled

I can tell my site responds to changes to Imfuscator.cs

now I need to connect the CSS calls to ../Content/Images/Imfuscator.ashx?i=123.13.123.asd to my image file located in the dll as a resource

below is the code from the ashx now being used in the .cs file.

what I need is to return the image for each css call to it.

 

Am I going the right way? Is all I need now is to work out the .cs file? How?

 

Thanks.

 

using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard;
using Orchard.Mvc.Filters;
using System;
using System.Reflection;
using System.Web.Caching;
using System.IO;

namespace testeorxinvent.Imfuscator
{
    public class Handler : System.Web.IHttpHandler
    {

        public void ProcessRequest(System.Web.HttpContext context)
        {
            Type streamerType = null;
            string path = context.Request.PhysicalPath.Replace("ashx", "dll");
            Cache cache = context.Cache;
            if (cache[path] != null)
                streamerType = (Type) cache[path];
            else
            {
                Assembly assembly = Assembly.Load(File.ReadAllBytes(path)); // Prevents file from being locked unlike .LoadFrom
                streamerType = assembly.GetType("testeorxinvent.Imfuscator.Streamer");
                cache.Add(path, streamerType, new CacheDependency(path), Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5), CacheItemPriority.Normal, null);
            }           
            streamerType.InvokeMember("StreamImage",
                                System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod,
                                null,
                                null,
                                new object[] { context });
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

    }
}

Jun 20, 2011 at 12:17 PM

You don't want Filters - they're for supplementing existing routes and controllers with additional processing.

You just want a Controller.

That's why I provided a link showing you how to return file data from a Controller :)

You can also implement an IRouteProvider if your URLs absolutely have to be ../Content/Images/Imfuscator.ashx. By setting up routes you can make any URL get processed by any controller you like.

Do you have the full source code? There are tons of examples of Controller and IRouteProvider in the existing source, and you really should just look at Sebastien's Media Folder module (http://orchardmediafolder.codeplex.com) for an actual example of a controller returning a file, although that uses FilePathResult where you just need to use the File(data[]) method.

 

Jun 21, 2011 at 9:37 AM

here's my controller file.

I'm trying to get the logo.png image returned. I guess I need some extra file to associate specific URL calls to this controller...

what do I need to get this hello world working?

using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard;
using Orchard.Mvc.Filters;
using System;
using System.Reflection;
using System.Web.Caching;
using System.IO;
using Orchard.ContentManagement;
using Orchard.Environment.Configuration;

namespace testeorxinvent.Controllers
{
    public class Imfuscator : Controller
    {
      private readonly IOrchardServices _orchardServices;
        private readonly ShellSettings _shellSettings;

        public Imfuscator(IOrchardServices orchardServices, ShellSettings shellSettings) {
            _orchardServices = orchardServices;
            _shellSettings = shellSettings;
        }

public ActionResult Image(string id)
{
    var path = @"C:\inetpub\wwwroot\Orchard_v11_5922\Themes\testeorxinvent\Content\Images\Logo.png";

return base.File(path, "image/png");

}


}
}

Jun 22, 2011 at 7:02 PM

Firstly, name your class "ImfuscatorController" rather than just "Imfuscator" - it's just the convention.

Also, add [HttpGet] attribute to your Image action - it might be the default but it's best to be explicit.

Then by default without any routing, the action should be on the URL /testeorxinvent/Imfuscator/Image/id (or /testeorxinvent/Imfuscator/Image?id=foo - can't remember without testing).

You can implement IRouteProvider to change the URL routing - search the Orchard source for existing examples, or look up MVC documentation, there are a lot of different ways to configure them.

Jun 28, 2011 at 12:52 AM

I just dumped my app folder into the Orchard.Web folder and overruled the web.config with a local one. All you need for .ashx  is this:

 

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers accessPolicy="Script,Read">
      <add name="SimpleHandlerFactory-Integrated" path="*.ashx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory" preCondition="integratedMode" />
    </handlers>
  </system.webServer>
</configuration>

 

Just look in Windows\System32\inetsrv\config\applicationHost.config for the handler if you need other ones, they should all be there. 

I am using a source version of orchard, I haven't tried a production version yet so I can't tell you if its different there. Regarding the dlls, I put them in the bin folder. You could use 'probing privatePath' to put them somewhere else.

 

Kind Regards

 

Jul 4, 2011 at 11:27 AM

thanks, this is just what I was looking for.

thanks to all for helping.

I can now open ashx files along with other resources, by adding this Web.config to /Content

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
  <system.web>
    <httpHandlers>
      <!-- iis6 - for any request in this location, return via managed static file handler -->
      <add path="*" verb="*" type="System.Web.StaticFileHandler" />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <handlers accessPolicy="Script,Read">
      <!--
      iis7 - for any request to a file exists on disk, return it via native http module.
      accessPolicy 'Script' is to allow for a managed 404 page.
      -->
      <add name="SimpleHandlerFactory-Integrated" path="*.ashx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory" preCondition="integratedMode" />
      <add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
    </handlers>
  </system.webServer>
</configuration>