WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

IIS Express で Windows 認証

by WebSurfer 2018年8月19日 14:10

IIS Express でも Visual Studio でプロジェクトのプロパティを設定することで Windows 認証が使えるという話を書きます。

プロパティの設定

上の画像は Visual Studio Community 2015 のテンプレートで[空]の Web Forms アプリケーションプロジェクトを作り、「ソリューションエクスプローラー」ウィンドウでプロジェクトを選択し、その「プロパティ」ウィンドウで[Windows 認証]の設定をデフォルトから変えて[有効]にしたところです。

上の画像では[匿名認証]の設定もデフォルトから変えて[無効]に設定していますが、そうしておかないと、匿名のままアクセスできてしまう(Windows 認証はスルーされてしまう)ので注意してください。

上記のように設定してから、Visual Studio で[デバッグ(D)]⇒[デバッグなしで開始(H)」(または[デバッグの開始(S)])をクリックすると Web アプリは IIS Express 上で実行され、以下の画像のように認証情報を入力するダイアログが表示されます。(画像は Windows 10 Pro 64-bit の IE11 のもの)

認証ダイアログ

そのダイアログに有効な Windows アカウント名とパスワードを入力して[OK]をクリックすれば認証は通って画面が表示されます。User.Identity.Name でアカウント名も取得できます。(そのあたりの動作は IIS を使った時のものと同じ)

実は自分は IIS Express で Windows 認証が使えることを知らなくて、わざわざローカル IIS を使って Windows 認証のテストをしてました。(汗)

MSDN Forum のスレッド「Request.ServerVariables("REMOTE_USER")が空の文字列を返す」の Q&A の際に調べて初めて知った次第です。

なお、Windows 認証は Windows の機能に依存するもので、IE と IIS の組み合わせでなければ実現できないそうですので注意してください。(他の組み合わせでできたとしてもたまたまで、Microsoft が保証しているわけではなさそうです)

Tags: ,

DevelopmentTools

サーバー側で応答コンテンツの取得

by WebSurfer 2018年7月14日 13:26

ASP.NET Web アプリの応答コンテンツの文字列を、サーバー側でのログ取得などの目的で取得する方法を書きます。(あまり需要はないかもしれませんが)

参考にしたのは stackoverflow のスレッド Logging raw HTTP request/response in ASP.NET MVC & IIS7 です。その記事を見れば情報としては十分かもしれませんが、リンク切れになると困るし、記事にはない HTTP モジュールを使っての設定方法も追加して書いておきます。

基本的には、応答コンテンツは応答ストリームに含まれますのでそれを何らかの手段で取得するということになります。

stackoverflow の記事では、応答ストリームをラッピングするフィルターを作成し、フィルターの中に応答ストリームとは別に MemoryStream を用意し、ASP.NET が応答ストリームに書き込む際 MemoryStream にも同じ内容を書き込むようにし(要するに MemoryStream にコピーを作成し)、MemoryStream から応答コンテンツを取得するという方法が紹介されています。

下のコード例を見てください。その中の OutputFilterStream クラスがラッピングフィルターです。stackoverflow の記事のコードをそのままコピーしたものです。

HTTP モジュール(下のコードでは ResponseContentLogHttpModule クラス)を用い、BeginRequest イベントのタイミングでラッピングフィルターを HttpResponse.Filter プロパティに設定します。(注:EndRequest のタイミングではダメです。ストリームにはその前に書き込まれるので)

HttpResponse.Filter プロパティの NSDN ライブラリの説明には "伝送する前に HTTP エンティティ本体を変更するために使用される、ラッピングフィルターオブジェクトを取得または設定します" とありますが、OutputFilterStream はコンテンツの変更は一切せずそのまま応答として返し、MemoryStream にコピーを取得することのみ行います。

BeginRequest のタイミングで設定したラッピングフィルターオブジェクトを EndRequest のタイミングで取得し MemoryStream にコピーされた応答コンテンツを取得します。

BeginRequest のタイミングから EndRequest のタイミングまでラッピングフィルターオブジェクトを保持するには HttpContext.Items プロパティを利用します。下のコード例を見てください。

クラスのフィールド等で保持するのは NG という報告がありましたので注意してください。HttpContext.Items を使えば、少なくとも HttpContext が存在する限りラッピングフィルターオブジェクトは保持されるが(GC の対象にはならないが)、クラスのフィールド変数ではその限りではないということのようです。

以下に HTTP モジュールとラッピングフィルターのコード例をアップしておきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;

// HTTP モジュール
public class ResponseContentLogHttpModule : IHttpModule
{
  public ResponseContentLogHttpModule()
  {
  }

  public String ModuleName
  {
    get { return "ResponseContentLogHttpModule"; }
  }

  public void Init(HttpApplication application)
  {
    application.BeginRequest += this.BeginRequest;
    application.EndRequest += this.EndRequest;
  }

  private void BeginRequest(Object source, EventArgs e)
  {
    HttpApplication application = (HttpApplication)source;
    HttpContext context = application.Context;
    string filePath = context.Request.FilePath;
    string fileExtension =
        VirtualPathUtility.GetExtension(filePath);

    // とりあえず .aspx のみ対象(.js, .css 等は対象外)
    if (fileExtension.Equals(".aspx"))
    {
      HttpResponse response = context.Response;
      var filter = new OutputFilterStream(response.Filter);
      response.Filter = filter;

      // OutputFilterStream オブジェクトを EndRequest で
      // 利用できるよう HttoContext.Items に参照を保持。
      // (注:クラスのフィールド等で保持するのは NG)
      context.Items["FilterStream"] = filter;
    }
  }

  private void EndRequest(Object source, EventArgs e)
  {
    HttpApplication application = (HttpApplication)source;
    HttpContext context = application.Context;
    string filePath = context.Request.FilePath;
    string fileExtension =
        VirtualPathUtility.GetExtension(filePath);

    // とりあえず .aspx のみ対象(.js, .css 等は対象外)
    if (fileExtension.Equals(".aspx"))
    {
      // BegineRequest で HttpContext.Items に保持した
      // OutputFilterStream オブジェクトへの参照を取得
      var filter =
        (OutputFilterStream)context.Items["FilterStream"];

      // 応答コンテンツの文字列を取得
      string responseContent = filter.ReadStream();
    }
  }

  // IHttpModule に定義されているので空でも以下が必要
  public void Dispose() { }
}

// ラッピングフィルタークラス
// stackoverflow の記事のコードをそのままコピー
public class OutputFilterStream : Stream
{
  private readonly Stream InnerStream;
  private readonly MemoryStream CopyStream;

  public OutputFilterStream(Stream inner)
  {
    this.InnerStream = inner;
    this.CopyStream = new MemoryStream();
  }

  public string ReadStream()
  {
    lock (this.InnerStream)
    {
      if (this.CopyStream.Length <= 0L ||
          !this.CopyStream.CanRead ||
          !this.CopyStream.CanSeek)
      {
        return String.Empty;
      }

      long pos = this.CopyStream.Position;
      this.CopyStream.Position = 0L;
      try
      {
        return new StreamReader(this.CopyStream).ReadToEnd();
      }
      finally
      {
        try
        {
          this.CopyStream.Position = pos;
        }
        catch { }
      }
    }
  }

  public override bool CanRead
  {
    get { return this.InnerStream.CanRead; }
  }

  public override bool CanSeek
  {
    get { return this.InnerStream.CanSeek; }
  }

  public override bool CanWrite
  {
    get { return this.InnerStream.CanWrite; }
  }

  public override void Flush()
  {
    this.InnerStream.Flush();
  }

  public override long Length
  {
    get { return this.InnerStream.Length; }
  }

  public override long Position
  {
    get { return this.InnerStream.Position; }
    set
    {
      this.CopyStream.Position =
      this.InnerStream.Position = value;
    }
  }

  public override int Read(byte[] buffer,
                          int offset, int count)
  {
    return this.InnerStream.Read(buffer, offset, count);
  }

  public override long Seek(long offset, SeekOrigin origin)
  {
    this.CopyStream.Seek(offset, origin);
    return this.InnerStream.Seek(offset, origin);
  }

  public override void SetLength(long value)
  {
    this.CopyStream.SetLength(value);
    this.InnerStream.SetLength(value);
  }

  public override void Write(byte[] buffer,
                             int offset, int count)
  {
    this.CopyStream.Write(buffer, offset, count);
    this.InnerStream.Write(buffer, offset, count);
  }
}

上記の HTTP モジュールが動くようにするには web.config での設定が必要ですので注意してください。以下に設定例を書いておきます。

<system.webServer>
  <modules>
    <add name="ResponseContentLogHttpModule" 
         type="ResponseContentLogHttpModule"/>
  </modules>
</system.webServer>

Tags: , ,

ASP.NET

Ajax Control Toolkit スクリプト バンドル

by WebSurfer 2018年6月10日 22:31

Ajax Control Toolkit は v7.x 以前では、使用する複数のスクリプトファイルをバンドルするために ToolkitScriptManager を使っていました。(ScriptManager ではなくて)

v15.1 以降では、DevExpress の記事 How to use Bundling and CDN にありますように、ScriptManager を使ってのバンドリングが可能となっています。(ちなみに、v15.1 以降 ToolkitScriptManager は提供されていません)

以下の画像の赤枠で囲った部分を見てください。これが Ajax Control Toolkit 用の複数のスクリプトファイルがバンドルされた結果です。

バンドルされたスクリプト

ASP.NET 4.5 以降で、Visual Studio Commnunity 2015 の[Web フォーム]のテンプレートを利用して作った ASP.NET Web Forms プロジェクトに、Ajax Control Toolkit を追加し、必要なスクリプトファイルを ScriptManager で統合できるようにしてみましたので、その方法や結果から分かったことを備忘録として書いておきます。

先の記事「ASP.NET 4.5 ScriptManage」に書きましたように、Microsoft Ajax、jQuery 等のスクリプトファイルが ScriptManager で統合できるようになり、[Web フォーム]のテンプレートを利用して作ったプロジェクトでは、それらが設定済みとなっています。

なので、そのプロジェクトに Ajax Control Toolkit 用のバンドル機能を追加すると、重複などの問題で修正が必要かも知れないと思っていましたが、自分が試した限りはそういうことはなかったです。

まず、NuGet で AjaxControlToolkit のパッケージをインストールします。それだけでも基本的なコントロールは動くようになりますが、ScriptManager を利用したバンドリングを可能にするためには、StaticResources パッケージも追加でインストールする必要があります。

NuGet パッケージの追加

バージョンは Ajax Control Toolkit 本体と合わせた方がよさそうです。ここでは、先にインストールした既存の Ajax Control Toolkit 本体が v17.1.1 なので、StaticResources のバージョンもそれに合わせて v17.1.1 にしました。

なお、Visual Studio で[空]のテンプレートを選択してプロジェクトを作った場合は StaticResources パッケージだけでは不足です。追加で Antlr, WebGrease などの NuGet パッケージもインストールする必要がありますが、StaticResources をインストールする際それらも自動的に追加されるはずです。

StaticResources のインストールが完了すると、以下の画像の赤枠で示したフォルダが生成され、その中に CSS ファイル、イメージ、スクリプトファイルが格納されます。

追加された CSS とスクリプト

Content/AjaxControlToolkit フォルダ下の Images フォルダには .gif, .jpg, .png などの画像ファイルが、Styles フォルダには minify してない .css ファイルと minify した .min.css ファイルの両方が格納されます。

Scripts/AjaxControlToolkit フォルダ下の Debug フォルダには minify してない .debug.js ファイルが、Release フォルダには minify した .js ファイルが格納されます。

さらに、web.config にはデフォルトで以下の設定が追加されます。

<ajaxControlToolkit 
    useStaticResources="true" 
    renderStyleLinks="false" />

DevExpress の記事 How to use Bundling and CDN の説明によるとバンドル機能をコントロールするもののようです。

あとは、DevExpress の記事 "Manual changes required" のセクションを参考に、ScriptManager に ScriptReference を追加してやればスクリプトは一番上の画像に示したようにバンドルされます。

具体的には以下の画像の赤枠で示したコードを追加してやります。

ScriptReference の追加

赤枠部分以外の ScriptManager のコードは、Visual Studio の[Web フォーム]のテンプレートを利用して作った ASP.NET Web Forms アプリのマスタページ Site.master に自動生成されたものです。

.css ファイルについても、DevExpress の記事に従って Styles.Render をページの適当な場所に追加してやれば、.css ファイルを参照する link 要素がレンダリングされます。

ただし、.css ファイルは web.config の compilation 要素で debug="false" とするか、Global.asax の Application_Start メソッドで BundleTable.EnableOptimizations を true に設定しないとバンドルされませんので注意してください。(スクリプトファイルと異なります。理由不明)

BundleTable.EnableOptimizations を true に設定する意味については Microsoft の文書 Bundling and Minification を見てください。抜粋すると "The following code (true に設定すること) enables bundling and minification and overrides any setting in the Web.config file." ということだそうです。

web.config の compilation 要素の debug 属性の true / false でどう異なるかですが、自分が検証した限りでは、

  • .css ファイル: true で minify &バンドルあり、false で minify なしでバンドルもなし。
  • スクリプトファイル: true / false の設定にかかわらず minify &バンドルあり。

・・・となりました。

スクリプトファイルの結果がちょっと解せないです。そのままでは、debug="true" にしてもスクリプトのデバッグができません。

.debug.js 版をダウンロードさせてデバッグ可能なようにするには、上の ScriptManager の設定のところで ScriptReference をコメントアウトする他なさそうです。

あと、minify&バンドルされたスクリプトファイルのサイズですが、何と 1,248,841 バイトもありました。Ajax Control Toolkit は使わないのにスクリプトをダウンロードするように設定するというのは避けた方がよさそうです。

ちなみに、.CSS ファイルの方は minify&バンドル後で 56,240 バイトでした。

最後にもう一つ、HtmlEditorEntender には AjaxControlToolkit.HtmlEditor.Sanitizer, HtmlAgilityPack が必要です。

それなしでは先の記事「Ajax Control Toolkit デモ」で述べたような意味不明のエラーが出てハマるかもしれませんのでご注意ください。

Tags: , ,

AJAX

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2018年11月  >>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar