miso_soup3 Blog

主に ASP.NET 関連について書いています。

IIS URL Rewrite の Outbound Rules で CSS の中身を書き換える

例えば、以下のように CSS の中にある URL を、IIS の URL Rewrite で書き換えたい場合の話です。

例:

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(/content/images/abc.png); background-image: url(/content/images/abc.png);
}

以下のように、URL 「/content」ではなく、「/replacedcontents19」に置き換えたい:

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(/replacedcontents19/images/abc.png); background-image: url(/replacedcontents19/images/abc.png);
}
Web.config の設定

以下のように設定します。

<rewrite>
  <outboundRules>
    <rule name="RewriteCSS" preCondition="ResponseIsCss">
      <match filterByTags="None" pattern="/content/([^&quot;\)]*)" />
      <action type="Rewrite" value="/replacedcontents19/{R:1}" />
    </rule>
    <preConditions>
      <preCondition name="ResponseIsCss">
        <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/css" />
      </preCondition>
    </preConditions>
  </outboundRules>
</rewrite>

CSSの場合は、filterByTags="None" と設定する必要があります。HTMLの場合は、URL Rewrite Module 2.0 Configuration Reference : The Official Microsoft IIS Site ここの「Pre-defined tags」を参照し、適切な値を設定します。

filterByTags="None"を設定した場合、コンテンツ全体を走査し置き換えたい箇所を特定しているので、負担がかかります。
ですので、対処として「rewriteBeforeCache」や「occurrences」の設定が用意されています(先のページを参照)。今回はそれは使用していません。

違う例

先ほどは、正規表現を、/content/([^"\)]*) と指定しています。
例えば、もしこの正規表現を、/content/(.*) とした場合、1行に複数個所該当する場合は、正しく置き換えられません。
以下のような CSS だと、

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(/content/images/abc.png); background-image: url(/content/images/abc.png);
}

.div1 {
    background-image: url(/content/images/abc.png);
}

次のように、最初と最後のurlだけがマッチされて、置き換えられます。
中盤の行末にある url は置き換えられません。

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(/replacedcontents19/images/abc.png); background-image: url(/content/images/abc.png);
}

.div1 {
    background-image: url(/replacedcontents19/images/abc.png);
}

正規表現(.*)が、行の最後までマッチしてしまっていることが原因だと思いますが、
正規表現がまるで”行ごと”に置き換えているような挙動の理由が、分からないままです。(=なぜ、3つめのurlは置換されているのか?)
→追記:正規表現において「.」は、改行コード以外の1文字を表すためだと思われます。

環境

環境は、IIS 10 と URL Rewrite Module 2.0 で試しました。
試したコードをこちらに置いておきます。test.css · GitHub

おまけ検証追加

検証1

CSSのセミコロンを無しで、同じ行に2回登場させてみる。
置き換える値を固定値にしてみる。
正規表現は(.*)。

Web.config:

      <outboundRules>
        <rule name="RewriteCSS" preCondition="ResponseIsCss">
          <match filterByTags="None" pattern="/content/(.*)" />
          <action type="Rewrite" value="replaced1" />
        </rule>

元のCSS:

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(/content/images/abc.png) url(/content/images/abc.png);
}
.div1 {
    background-image: url("/content/images/abc.png");
}

.div6 {
    background-image: url(/content/images/content/abc.png);
}

.div7 {
    background-image: url("/content/images/content/abc.png");
}

結果、置き換えた後のCSS:

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(replaced1
}
.div1 {
    background-image: url("replaced1
}

.div6 {
    background-image: url(replaced1
}

.div7 {
    background-image: url("replaced1
}
検証2

先ほどと同じ条件。
・CSSのセミコロンを無しで、同じ行に2回登場させてみる。
・置き換える値を固定値にしてみる。
・正規表現は(.*)。
これで、occurrences="1" を追記してみる。

web.config:

      <outboundRules>
        <rule name="RewriteCSS" preCondition="ResponseIsCss">
          <match filterByTags="None" pattern="/content/(.*)" occurrences="1"/>
          <action type="Rewrite" value="replaced2" />
        </rule>

元のCSS:

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(/content/images/abc.png) url(/content/images/abc.png);
}
.div1 {
    background-image: url("/content/images/abc.png");
}

結果、置き換えられたCSS:

body {
    padding-top: 50px;
    padding-bottom: 223px;
    background-image: url(replaced2
}
.div1 {
    background-image: url("/content/images/abc.png");
}

メモ API Mock Server

API 記述言語で定義しておけば後でどうにでもなる? Swagger Editor の Generate Server は、いろんなサーバーがあるけど静的生成。 モックからプロトタイプになるにつれ状況は変わりそう。