Использование Azure CDN для динамических страниц

Как мы знаем, микрософт предлагает невероятные возможности по распространению контента во все точки мира, что заметно уменьшает время доступа к контенту. Это CDN. Как я писал ранее это сокращает трафик между континентами (бэкбонами). Но, чтобы включить CDN у простого сайта есть несколько особенностей, о которых напишу под катом, так как не достаточно просто включить CDN в панели управления и настроить CNAME домена.
1) Надо понимать, что если мы выставили context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(10));//10min и сделали запрос через CDN, то результат этого запроса изменится не ранее чем через 10 минут.
2) В проекте создаем папку /cdn/ и настраиваем модуль rewrite для простой переброски всех запросов приходящих запросов следующим образом:
Copy Source | Copy HTML
  1. <system.webServer>
  2.   <rewrite>
  3.       <rules>
  4.         <rule name="0" stopProcessing="false">
  5.           <match url="^(?:cdn/)(.*)$"/>
  6.           <action type="Rewrite" url="{R:1}" appendQueryString="true"/>
  7.         </rule>
  8.     </rewrite>
  9.   </system.webServer>

3) Далее мы хотим, чтобы все наши ответы передавались в сжатом виде при помощи GZIP. Напомню, что GZIP отличается от deflate тем, что у последнего отсутствует избыточных заголовок. Итак, добавляем в проект модуль сжатия и настраиваем сжатие CompressionModule:
Copy Source | Copy HTML
  1. namespace Modules
  2. {
  3.     using System;
  4.     using System.IO;
  5.     using System.IO.Compression;
  6.     using System.Text;
  7.     using System.Text.RegularExpressions;
  8.     using System.Web;
  9.     using System.Web.UI;
  10.     using System.Net;
  11.     using System.Net.Sockets;
  12.     /// <summary>
  13.     /// Compresses the output using standard gzip/deflate.
  14.     /// </summary>
  15.     public sealed class CompressionModule : IHttpModule
  16.     {
  17.         #region Constants and Fields
  18.         /// <summary>
  19.         /// The deflate string.
  20.         /// </summary>
  21.         private const string Deflate = "deflate";
  22.         /// <summary>
  23.         /// The gzip string.
  24.         /// </summary>
  25.         private const string Gzip = "gzip";
  26.         #endregion
  27.         #region Public Methods
  28.         /// <summary>
  29.         /// Compresses the response stream using either deflate or gzip depending on the client.
  30.         /// </summary>
  31.         /// <param name="context">
  32.         /// The HTTP context to compress.
  33.         /// </param>
  34.         public static void CompressResponse(HttpContext context)
  35.         {
  36.             //Делаем так как у нас проксирование через CDN
  37.             context.Response.Cache.SetCacheability(HttpCacheability.Public);
  38.             context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(10));//10min
  39.             System.Diagnostics.Debug.WriteLine("compressing --->" + context.Request.Path);
  40.             if (IsEncodingAccepted(Deflate))
  41.             {
  42.                 context.Response.Filter = new DeflateStream(context.Response.Filter, CompressionMode.Compress);
  43.                 WillCompressResponse = true;
  44.                 SetEncoding(Deflate);
  45.             }
  46.             else if (IsEncodingAccepted(Gzip))
  47.             {
  48.                 context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
  49.                 WillCompressResponse = true;
  50.                 SetEncoding(Gzip);
  51.             }
  52.         }
  53.         #endregion
  54.         #region Private Methods
  55.         private static bool WillCompressResponse
  56.         {
  57.             get
  58.             {
  59.                 HttpContext context = HttpContext.Current;
  60.                 if (context == null) { return false; }
  61.                 return context.Items["will-compress-resource"] != null && (bool)context.Items["will-compress-resource"];
  62.             }
  63.             set
  64.             {
  65.                 HttpContext context = HttpContext.Current;
  66.                 if (context == null) { return; }
  67.                 context.Items["will-compress-resource"] = value;
  68.             }
  69.         }
  70.         #endregion
  71.         #region Implemented Interfaces
  72.         #region IHttpModule
  73.         /// <summary>
  74.         /// Disposes of the resources (other than memory) used by the module 
  75.         ///     that implements <see cref="T:System.Web.IHttpModule"></see>.
  76.         /// </summary>
  77.         void IHttpModule.Dispose()
  78.         {
  79.             // Nothing to dispose; 
  80.         }
  81.         /// <summary>
  82.         /// Initializes a module and prepares it to handle requests.
  83.         /// </summary>
  84.         /// <param name="context">
  85.         /// An <see cref="T:System.Web.HttpApplication"></see> 
  86.         ///     that provides access to the methods, properties, and events common to 
  87.         ///     all application objects within an ASP.NET application.
  88.         /// </param>
  89.         void IHttpModule.Init(HttpApplication context)
  90.         {
  91.             context.PreRequestHandlerExecute += ContextPostReleaseRequestState;
  92.             context.Error += new EventHandler(context_Error);
  93.         }
  94.         void context_Error(object sender, EventArgs e)
  95.         {
  96.             HttpContext context = ((HttpApplication)sender).Context;
  97.             Exception ex = context.Server.GetLastError();
  98.             // If this CompressionModule will be compressing the response and an unhandled exception
  99.             // has occurred, remove the WebResourceFilter as that will cause garbage characters to
  100.             // be sent to the browser instead of a yellow screen of death.
  101.             if (WillCompressResponse)
  102.             {
  103.                 context.Response.Filter = null;
  104.                 WillCompressResponse = false;
  105.             }
  106.         }
  107.         #endregion
  108.         #endregion
  109.         #region Methods
  110.         /// <summary>
  111.         /// Checks the request headers to see if the specified
  112.         ///     encoding is accepted by the client.
  113.         /// </summary>
  114.         /// <param name="encoding">
  115.         /// The encoding.
  116.         /// </param>
  117.         /// <returns>
  118.         /// The is encoding accepted.
  119.         /// </returns>
  120.         private static bool IsEncodingAccepted(string encoding)
  121.         {
  122.             var context = HttpContext.Current;
  123.             return context.Request.Headers["Accept-encoding"] != null &&
  124.                    context.Request.Headers["Accept-encoding"].Contains(encoding);
  125.         }
  126.         /// <summary>
  127.         /// Adds the specified encoding to the response headers.
  128.         /// </summary>
  129.         /// <param name="encoding">The encoding.</param>
  130.         private static void SetEncoding(string encoding)
  131.         {
  132.             HttpContext.Current.Response.AppendHeader("Content-encoding", encoding);
  133.         }
  134.         /// <summary>
  135.         /// Handles the BeginRequest event of the context control.
  136.         /// </summary>
  137.         /// <param name="sender">
  138.         /// The source of the event.
  139.         /// </param>
  140.         /// <param name="e">
  141.         /// The <see cref="System.EventArgs"/> instance containing the event data.
  142.         /// </param>
  143.         private static void ContextPostReleaseRequestState(object sender, EventArgs e)
  144.         {
  145.             var context = ((HttpApplication)sender).Context;
  146.             System.Diagnostics.Debug.WriteLine("precessing request --->" + context.Request.Path);
  147.             // only when page is requested 
  148.             if (context.CurrentHandler is Page &&
  149.                 context.Request["_TSM_HiddenField_"] == null &&
  150.                 context.Request["HTTP_X_MICROSOFTAJAX"] == null &&
  151.                 context.Request.HttpMethod == "GET")
  152.             {
  153.                 CompressResponse(context);
  154.             }
  155.         }
  156.         #endregion
  157.     }
  158. }

Copy Source | Copy HTML
  1.   <system.webServer>
  2.     <validation validateIntegratedModeConfiguration="false"/>
  3.     <staticContent>
  4.       <remove fileExtension=".js" />
  5.       <mimeMap fileExtension=".js" mimeType="text/javascript" />
  6.       <remove fileExtension=".css" />
  7.       <mimeMap fileExtension=".css" mimeType="text/css" />
  8.       <remove fileExtension=".ico"/>
  9.       <mimeMap fileExtension=".ico" mimeType="image/x-icon"/>
  10.       <remove fileExtension=".gif"/>
  11.       <mimeMap fileExtension=".gif" mimeType="image/gif"/>
  12.       <remove fileExtension=".woff"/>
  13.       <mimeMap fileExtension=".woff" mimeType="application/x-font-woff" />
  14.       <remove fileExtension=".svg"/>
  15.       <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
  16.       <clientCache cacheControlCustom="public" cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
  17.     </staticContent>
  18.     <httpCompression
  19.         directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files"
  20.         cacheControlHeader="max-age=86400"
  21.         noCompressionForHttp10="false"
  22.         noCompressionForProxies="false"
  23.         noCompressionForRange="false"
  24.         sendCacheHeaders="true" minFileSizeForComp="0">
  25.       <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
  26.       <dynamicTypes>
  27.         <add mimeType="text/*" enabled="true" />
  28.         <add mimeType="message/*" enabled="true" />
  29.         <add mimeType="application/x-javascript" enabled="true" />
  30.         <add mimeType="*/*" enabled="false" />
  31.       </dynamicTypes>
  32.       <staticTypes>
  33.         <add mimeType="text/*" enabled="true" />
  34.         <add mimeType="message/*" enabled="true" />
  35.         <add mimeType="application/javascript" enabled="true" />
  36.         <add mimeType="*/*" enabled="false" />
  37.       </staticTypes>
  38.     </httpCompression>
  39.     <urlCompression doStaticCompression="true" doDynamicCompression="true" />
  40.     <modules runAllManagedModulesForAllRequests="true">
  41.       <add name="CompressionModule" type="Modules.CompressionModule"/>
  42.     </modules>
  43. </system.webServer>

4) Сразу особенность в том, что в CompressionModule мы не включаем сжатие для context.Request["_TSM_HiddenField_"] == null && context.Request["HTTP_X_MICROSOFTAJAX"] - это Ajax скрипты для AjaxControlToolkit и прочего от микрософта.
5) И в завершение нам необходимо сделать перезапись поля action у aspForm, так как при олучении обращения через CDN оно будет сформировано как "/cdn/page.aspx" , что при выполнении PostBack будет ошибка. Перезаписать action можно с помощью RewriteFormHtmlTextWriter, который вставляется в masterpage как
Copy Source | Copy HTML
  1. protected override void Render(HtmlTextWriter writer)
  2. {
  3.     base.Render(new RewriteFormHtmlTextWriter(writer));
  4. }
Copy Source | Copy HTML
  1. namespace top30{
  2.     using System.IO;
  3.     using System.Web;
  4.     using System.Web.UI;
  5.     /// <summary>
  6.     /// The RewriteFormHtmlTextWriter class implements Form action tag rewriting for rewritten pages 
  7.     ///     on Mono.
  8.     /// </summary>
  9.     public class RewriteFormHtmlTextWriter : HtmlTextWriter
  10.     {
  11.         #region Constructors and Destructors
  12.         /// <summary>
  13.         /// Initializes a new instance of the <see cref="RewriteFormHtmlTextWriter"/> class.
  14.         /// </summary>
  15.         /// <param name="writer">
  16.         /// The writer.
  17.         /// </param>
  18.         public RewriteFormHtmlTextWriter(Html32TextWriter writer)
  19.             : base(writer)
  20.         {
  21.             this.InnerWriter = writer.InnerWriter;
  22.         }
  23.         /// <summary>
  24.         /// Initializes a new instance of the <see cref="RewriteFormHtmlTextWriter"/> class.
  25.         /// </summary>
  26.         /// <param name="writer">
  27.         /// The writer.
  28.         /// </param>
  29.         public RewriteFormHtmlTextWriter(TextWriter writer)
  30.             : base(writer)
  31.         {
  32.             this.InnerWriter = writer;
  33.         }
  34.         #endregion
  35.         #region Public Methods
  36.         /// <summary>
  37.         /// Writes the specified markup attribute and value to the output stream, and, if specified, writes the value encoded.
  38.         /// </summary>
  39.         /// <param name="name">
  40.         /// The markup attribute to write to the output stream.
  41.         /// </param>
  42.         /// <param name="value">
  43.         /// The value assigned to the attribute.
  44.         /// </param>
  45.         /// <param name="encode">
  46.         /// true to encode the attribute and its assigned value; otherwise, false.
  47.         /// </param>
  48.         public override void WriteAttribute(string name, string value, bool encode)
  49.         {
  50.                 if (name == "action")
  51.                 {
  52.                     if (HttpContext.Current.Items["ActionAlreadyWritten"] == null)
  53.                     {
  54.                         value = HttpContext.Current.Request.RawUrl.Replace("/cdn/","/");
  55.                         HttpContext.Current.Items["ActionAlreadyWritten"] = true;
  56.                     }
  57.                 }
  58.             base.WriteAttribute(name, value, encode);
  59.         }
  60.         #endregion
  61.     }
  62. }

Метки:   Категории:microsoft | Code


blog comments powered by Disqus

Добавить комментарий

Кто я?

Программист. Я слежу за блогосферой и знаю, как будет развиваться интернет. Когда у меня есть время я даже прилагаю для этого усилия. Подробнее

Последние комментарии

Не отображать

Topbot at FeedsBurner

Облако тэгов

Мои Твиты

Twitter апреля 16, 18:40
Теория Кварков (Стандартная модель элементарных частиц) http://dlvr.it/R2xPxN https://twitter.com/f1ashr/status/1118222539942064128/photo/1

Twitter апреля 12, 09:18
Апокалипсис сегодня в России http://dlvr.it/R2gHxT

Twitter апреля 9, 12:15
Категории роликов Ютуба http://dlvr.it/R2Sdbm https://twitter.com/f1ashr/status/1115588934078046208/photo/1

Twitter апреля 9, 12:15
О текущих проблемах http://dlvr.it/R2SdbC

Twitter апреля 9, 09:12
В Подмосковье печатают собственные деньги http://dlvr.it/R2S4ym https://twitter.com/f1ashr/status/1115542883531087873/photo/1

Twitter апреля 7, 12:02
Westeros World http://dlvr.it/R2L096 https://twitter.com/f1ashr/status/1114860890312941574/photo/1

Twitter апреля 5, 23:58
Само-задвигающиеся офисные стулья http://dlvr.it/R2GJ7h https://twitter.com/f1ashr/status/1114316304326975489/photo/1

Twitter апреля 5, 23:58
кибервойна http://dlvr.it/R2GJ7B https://twitter.com/f1ashr/status/1114316299457286144/photo/1

Twitter апреля 5, 23:58
Что происходит в Славянске? http://dlvr.it/R2GJ7g

Twitter апреля 3, 08:53
Что такое Дейтерий, Тритий и Тяжёлая вода http://dlvr.it/R24TNR https://twitter.com/f1ashr/status/1113363772813377537/photo/1

Twitter марта 20, 20:32
⚫️Тематика черного цвета для России http://dlvr.it/R1DKm7 https://twitter.com/f1ashr/status/1108466259765555200/photo/1

Twitter марта 20, 20:32
Автоматическое информирование о выходе новых сезонов http://dlvr.it/R1DKlZ

Twitter марта 20, 20:32
Голосовой набор тэгов http://dlvr.it/R1DKlv

Twitter марта 19, 20:27
Ради дружбы с Индией http://dlvr.it/R18YMd

Twitter марта 19, 20:27
Вконтакте 7 лет http://dlvr.it/R18YKm

Twitter марта 19, 20:27
Разбор фильма Дивергент2 Инсургент http://dlvr.it/R18YLy https://twitter.com/f1ashr/status/1108102729656762369/photo/1

Twitter марта 15, 12:51
Фильтр по стране на Ютубе http://dlvr.it/R0tgZq https://twitter.com/f1ashr/status/1106538299487739905/photo/1

Twitter марта 14, 12:25
Роскомнадзор и Криптовалюты http://dlvr.it/R0pqG6

Twitter марта 5, 19:59
Россияне встречают рассвет индийской йогой http://dlvr.it/R0DszD https://twitter.com/f1ashr/status/1103022131506425856/photo/1

Twitter февраля 24, 09:53
Что интересного есть в греческом языке? http://dlvr.it/QzZ2dK

Мой твиттер

Копирайт

Все мысли, высказанные в блоге, являются моим мнением и за это мнение меня никто не забанит! Кроме того, никто не имеет право копировать материалы блога без использования ctrl+C/V!

© Copyright 2008