返璞归真:动态图像生成,ASP.NET控制器,路由,IHttpHandlers和runAllManagedModulesForAllRequests

Warning, this is long but full of info. Read it all.

警告,这很长,但是充满了信息。 阅读全部。

Often folks want to dynamically generate stuff with ASP.NET. The want to dynamically generate PDFs, GIFs, PNGs, CSVs, and lots more. It's easy to do this, but there's a few things to be aware of if you want to keep things as simple and scalable as possible.

人们通常希望使用ASP.NET动态生成内容。 想要动态生成PDF,GIF,PNG,CSV等。 这很容易做到,但是如果您想使事情尽可能简单和可扩展,则需要注意一些事项。

You need to think about the whole pipeline as any HTTP request comes in. The goal is to have just the minimum number of things run to do the job effectively and securely, but you also need to think about "who sees the URL and when."

您需要考虑所有HTTP请求进入时的整个管道。目标是仅运行最少数量的事情来有效,安全地完成工作,但是您还需要考虑“谁看到URL和何时看到。 ”

A timeline representation of the ASP.NET pipeline

This diagram isn't meant to be exhaustive, but rather give a general sense of when things happen.

该图并不意味着要详尽无遗,而是可以大致了解发生情况的时间。

Modules can see any request if they are plugged into the pipeline. There are native modules written in C++ and managed modules written in .NET. Managed modules are run anytime a URL ends up being processed by ASP.NET or if "RAMMFAR" is turned on.

如果将模块插入管道,则可以看到任何请求。 有C ++编写的本机模块和.NET编写的托管模块。 只要最终由ASP.NET处理URL打开“ RAMMFAR”,就可以运行托管模块。

RAMMFAR means "runAllManagedModulesForAllRequests" and refers to this optional setting in your web.config.

RAMMFAR的意思是“ runAllManagedModulesForAllRequests”,并在您的web.config中引用此可选设置。

<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>

You want to avoid having this option turned on if your configuration and architecture can handle it. This does exactly what it says. All managed modules will run for all requests. That means *.* folks. PNGs, PDFs, everything including static files ends up getting seen by ASP.NET and the full pipeline. If you can let IIS handle a request before ASP.NET sees it, that's better.

如果您的配置和体系结构可以处理,则要避免打开此选项。 这就是它所说的。 所有托管模块将针对所有请求运行。 这意味着*。*乡亲。 PNG,PDF以及包括静态文件在内的所有内容最终都会被ASP.NET和完整的管道所看到 如果可以让IIS在ASP.NET看到请求之前处理该请求,那更好。

Remember that the key to scaling is to do as little as possible. You can certainly make a foo.aspx in ASP.NET Web Forms page and have it dynamically generate a graphic, but there's some non-zero amount of overhead involved in the creation of the page and its lifecycle. You can make a MyImageController in ASP.NET MVC but there's some overhead in the Routing that chopped up the URL and decided to route it to the Controller. You can create just an HttpHandler or ashx. The result in all these cases is that an image gets generated but if you can get in and get out as fast as you can it'll be better for everyone. You can route the HttpHandler with ASP.NET Routing or plug it into web.config directly.

请记住,扩展的关键是尽可能少地做。 您当然可以在ASP.NET Web窗体页面中创建foo.aspx,并使其动态生成图形,但是在页面创建及其生命周期中涉及的开销不为零。 您可以在ASP.NET MVC中创建MyImageController,但是“路由”中存在一些开销,这些开销会切碎URL并决定将其路由到Controller。 您可以只创建一个HttpHandler或ashx。 在所有这些情况下,结果都是生成图像,但是如果您能尽快进入和退出,则对每个人都更好。 您可以使用ASP.NET Routing路由HttpHandler或将其直接插入web.config。

可以但是...具有RAMMFAR和ASP.NET MVC的动态图像 (Works But...Dynamic Images with RAMMFAR and ASP.NET MVC)

A customer wrote me who was using ASP.NET Routing (which is an HttpModule) and a custom routing handler to generate images like this:

一位客户给我写信,他使用ASP.NET Routing(这是一个HttpModule)和一个自定义路由处理程序来生成如下图像:

routes.Add(new Route("images/mvcproducts/{ProductName}/default.png", 
new CustomPNGRouteHandler()));

Then they have a IRouteHandler that just delegates to an HttpHandler anyway:

然后,他们有了一个IRouteHandler,无论如何它只是委托给HttpHandler:

public class CustomPNGRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CustomPNGHandler(requestContext);
}
}

Note the {ProductName} route data in the route there. The customer wants to be able to put anything in that bit. if I visit http://localhost:9999/images/mvcproducts/myproductname/default.png I see this image...

注意那里的路由中的{ProductName}路由数据。 客户希望能够放入任何东西。 如果我访问http:// localhost:9999 / images / mvcproducts / myproductname / default.png我看到此图像...

Generated from this simple HttpHandler:

从这个简单的HttpHandler生成:

public class CustomPNGHandler : IHttpHandler
{
public bool IsReusable { get { return false; } }
protected RequestContext RequestContext { get; set; }

public CustomPNGHandler():base(){}

public CustomPNGHandler(RequestContext requestContext)
{
this.RequestContext = requestContext;
}

public void ProcessRequest(HttpContext context)
{
using (var rectangleFont = new Font("Arial", 14, FontStyle.Bold))
using (var bitmap = new Bitmap(320, 110, PixelFormat.Format24bppRgb))
using (var g = Graphics.FromImage(bitmap))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
var backgroundColor = Color.Bisque;
g.Clear(backgroundColor);
g.DrawString("This PNG was totally generated", rectangleFont, SystemBrushes.WindowText, new PointF(10, 40));
context.Response.ContentType = "image/png";
bitmap.Save(context.Response.OutputStream, ImageFormat.Png);
}
}
}

The benefits of using MVC is that handler is integrated into your routing table. The bad thing is that doing this simple thing requires RAMMFAR to be on. Every module sees every request now so you can generate your graphic. Did you want that side effect? The bold is to make you pay attention, not scare you. But you do need to know what changes you're making that might affect the whole application pipeline.

使用MVC的好处是处理程序已集成到路由表中。 糟糕的是,执行此简单操作需要打开RAMMFAR。 每个模块现在都能看到每个请求,因此您可以生成图形。 你想要那种副作用吗? 大胆是为了让您注意而不是吓scar您。 但是您确实需要知道正在进行哪些更改,这些更改可能会影响整个应用程序管道。

(As an aside, if you're a big site doing dynamic images, you really should have your images on their own cookieless subdomain in the cloud somewhere with lots of caching, but that's another article).

(顺便说一句,如果您是一个做动态图像的大型网站,您确实应该将图像放置在云中自己的无cookie子域中,该子域中有很多缓存,但这是另一篇文章)。

So routing to an HttpHandler (or an MVC Controller) is an OK solution but it's worth exploring to see if there's an easier way that would involve fewer moving parts. In this case the they really want the file to have the extension *.png rather than *.aspx (page) or *.ashx (handler) as it they believe it affects their image's SEO in Google Image search.

因此,路由到HttpHandler(或MVC控制器)是一个不错的解决方案,但是值得探索一下是否有一种更简单的方法来减少运动部件。 在这种情况下,他们确实希望文件具有* .png扩展名,而不是* .aspx(页面)或* .ashx(处理程序)扩展名,因为他们认为文件会影响其在Google图片搜索中图片的SEO。

更好:自定义HttpHandlers (Better: Custom HttpHandlers)

Remember that HttpHandlers are targeted to a specific path, file or wildcard and HttpModules are always watching. Why not use an HttpHandler directly and plug it in at the web.config level and set runAllManagedModulesForAllRequests="false"?

请记住,HttpHandlers定位于特定路径,文件或通配符,并且HttpModules始终在监视。 为什么不直接使用HttpHandler并将其插入web.config级别并设置runAllManagedModulesForAllRequests =“ false”?

<system.webServer>
<handlers>
<add name="pngs" verb="*" path="images/handlerproducts/*/default.png"
type="DynamicPNGs.CustomPNGHandler, DynamicPNGs" preCondition="managedHandler"/>
</handlers>
<modules runAllManagedModulesForAllRequests="false" />
</system.webServer>

Note how I have a * there in part of the URL? Let's try hitting http://localhost:37865/images/handlerproducts/myproductname/default.png. It still works.

请注意,我在网址的一部分中有一个*吗? 让我们尝试访问http:// localhost:37865 / images / handlerproducts / myproductname / default.png。 它仍然有效。

This lets us not only completely bypass the managed ASP.NET Routing system but also remove RAMMFAR so fewer modules are involved for other requests. By default, managed modules will only run for requests that ended up mapped to the managed pipeline and that's almost always requests with an extension. You may need to be aware of routing if you have a "greedy route" that might try to get ahold of your URL. You might want an IgnoreRoute. You also need to be aware of modules earlier in the process that have a greedy BeginRequest.

这样,我们不仅可以完全绕过托管的ASP.NET路由系统,还可以删除RAMMFAR,从而使其他请求所涉及的模块更少。 默认情况下,托管模块仅对最终映射到托管管道的请求运行,并且几乎总是带有扩展名的请求。 如果您的“贪婪路由”可能试图获取您的URL,则可能需要注意路由。 您可能需要一个IgnoreRoute。 您还需要在流程的早期阶段意识到带有贪婪的BeginRequest的模块。

The customer could setup ASP.NET and IIS to route request for *.png to ASP.NET, but why not be as specific as possible so that the minimum number of requests is routed through the managed pipeline? Don't do more work than you need to.

客户可以设置ASP.NET和IIS以将对* .png的请求路由到ASP.NET,但为什么不尽可能具体,以使最少数量的请求通过托管管道进行路由? 不要做比您需要做的更多的工作。

无扩展网址呢? (What about extensionless URLs?)

Getting extensionless URLs working on IIS6 was tricky before and lots of been written on it. Early on in IIS6 and ASP.NET MVC you'd map everything *.* to managed code. ASP.NET Routing used to require RAMFARR set to true until the Extensionless URL feature was created.

在IIS6上使无扩展URL正常工作是棘手的,并且在它上面已经写了很多东西。 在IIS6和ASP.NET MVC的早期,您将所有*。*映射到托管代码。 在创建无扩展URL功能之前,ASP.NET路由曾经要求将RAMFARR设置为true。

Extentionless URLs support was added in this KB http://support.microsoft.com/kb/980368 and ships with ASP.NET MVC 4. If you have ASP.NET MVC 4, you have Extentionless URLs on your development machine. But your server may not. You may need to install this hotfix, or turn on RAMMFAR. I would rather you install the update than turn on RAMMFAR if you can avoid it. The Run All Modules options is really a wildcard mapping.

此文件夹http://support.microsoft.com/kb/980368中添加了对Extentless URL的支持,并且随ASP.NET MVC 4一起提供。如果您具有ASP.NET MVC 4,则在开发计算机上具有Extensionless URL。 但是您的服务器可能没有。 您可能需要安装此修补程序,或打开RAMMFAR。 如果可以避免,我宁愿您安装此更新,也不愿打开RAMMFAR。 “运行所有模块”选项实际上是一个通配符映射。

Extensionless URLs exists so you can have URLs like /home/about and not /home/about.aspx. It exists to get URLs without extensions to be seen be the managed pipelines while URLs with extensions are not seen any differently. The performance benefits of Extensionless URLs over RAMMFAR are significant.

存在无扩展名的URL,因此您可以拥有/ home / about之类的URL,而没有/home/about.aspx之类的URL。 它的存在是为了使不带扩展名的URL被视为托管管道,而带扩展名的URL则没有任何不同。 与RAMMFAR相比,无扩展URL的性能优势非常明显。

If you have static files like CSS, JS and PNG files you really want those to be handled by IIS (and HTTP.SYS) for speed. Don't let your static files get mapped to ASP.NET if you can avoid it.

如果您有静态文件(如CSS,JS和PNG文件),则您确实希望由IIS(和HTTP.SYS)处理这些文件以提高速度。 如果可以避免的话,不要让静态文件映射到ASP.NET。

结论 (Conclusion)

When you're considering any solution within the ASP.NET stack (or "One ASP.NET" as I like to call it)...

当您考虑在ASP.NET堆栈(或我喜欢称之为“一个ASP.NET”)中的任何解决方案时...

The complete ASP.NET stack with MVC, Web Pages, Web Forms and more called out in a stack of boxes

...remember that it's things like IHttpHandler that sit at the bottom and serve one request (everything comes from IHttpHandler) while it's IHttpModule that's always watching and can see every request.

...记住,像IHttpHandler这样位于底部并处理一个请求(一切都来自IHttpHandler)的东西,而IHttpModule始终在监视并且可以看到每个请求。

In other words, and HttpHandler sees the ExecuteRequestHandler event which is just one event in the pipeline, while HttpModules can see every event they subscribe to.

换句话说,HttpHandler看到ExecuteRequestHandler事件只是管道中的一个事件,而HttpModules可以看到他们订阅的每个事件。

HttpHandlers and Modules are at the bottom of the stack

I hope this helps!

我希望这有帮助!

翻译自: https://www.hanselman.com/blog/back-to-basics-dynamic-image-generation-aspnet-controllers-routing-ihttphandlers-and-runallmanagedmodulesforallrequests

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值