WebSurfer's Home

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

IIS Express をインストールしました

by WebSurfer 2012年10月8日 16:10

今さらながらですが、IIS 7.5 Express を Microsoft の Download Center から入手してインストールして使ってみました。

IIS Express

IIS Express の概要の説明は、ここに書くまでもないので省略します。知りたい方は、ScottGu 氏のブログの翻訳 IIS Express の紹介 を見てください。手抜きですみません。(笑) 

インストールの仕方も、ここに書くまでもないので省略します。ダウンロードしたファイルをエクスプローラーで探して、それをダブルクリックすれば自動的にインストールされます。

ただし、インストールしただけでは Visual Studio 2010 SP1 から自動的に使えるようにはなりませんので注意してください。以下の画像にあるように、ソリューションエクスプローラーで、プロジェクトを右クリックして出てくるコンテキストメニューで、[IIS Express を使用する (U)...]に設定してやる必要があります。その他の詳細については、ScottGu 氏の別のブログ VS 2010 SP1 (Beta) and IIS Express を参考にしてください。

IIS Express を使用する

ところで、一番気になっていた IIS とワーカープロセスのアカウントですが、少なくともデフォルトでは、ASP.NET 開発サーバーと同じでした。即ち、その PC にログインしているユーザーのアカウントになります。

少なくとも自分が探した限りでは、IIS Express のワーカープロセスのアカウントを、IIS と同じにするオプションは見つかりませんでした。なので、ASP.NET 開発サーバーでは動いていたのが IIS では動かないという問題は、IIS Express でも起こり得るので注意が必要です。

ちなみに、IIS の匿名ユーザーアカウント、ワーカープロセスのアカウント/グループについては以下のページを参照してください。

IIS 7.0 での組み込みユーザーとグループ アカウントとは

Application Pool Identities

IIS 7 のアクセス許可

ASP.NET 開発サーバーおよび IIS Express の場合は、上記のページで説明されている IIS のアカウント/グループが、すべて PC にログインしているユーザーのものになります。

その他、気をつけなければならないのがアプリケーション名の設定です。具体的にどういうことかと言うと、以下のとおりです。

Web サイトプロジェクトで、例えば、物理パス C:\WebSite\AppDirectory に Web アプリケーションがあるとします。

Visual Studio から [ファイル(F)] ⇒ [Web サイトを開く(E)] で表示されるダイアログから Web アプリケーションを開いて、そのルート直下の Default.aspx を要求する場合、デフォルトでは以下のような URL になります。

ASP.NET 開発サーバ: http://localhost:xxxxx/AppDirectory/Default.aspx
IIS Express: http://localhost:xxxxx/Default.aspx
ローカルIIS: http://<サイト名>/<アプリ名のエイリアス>/Default.aspx

「ASP.NET 開発サーバ」と「ローカル IIS」の場合は AppDirectory と <アプリ名のエイリアス> の名前を同じに設定すれば "/<アプリ名のエイリアス>/default.aspx" というサイトルート相対パスが使えます。

ところが、「IIS Express」の場合は "/<アプリ名のエイリアス>/default.aspx" というサイトルート相対パスは使えません。パスの設定によってはリンクが切れて、「ASP.NET 開発サーバー」や「ローカル IIS」で動いていたものが「IIS Express」では動かない(または、その逆)ということが起こり得ます。

Web アプリケーションプロジェクトの場合は「IIS Express」を使って /<アプリ名のエイリアス>/default.aspx というように設定する方法があるのは見つけましたが、Web サイトプロジェクトの方はギブアップしました。

なので、自分が設定する方法を知らないだけという可能性は否定し切れません。(笑)

Tags:

DevelopmentTools

非同期 HTTP ハンドラ

by WebSurfer 2012年9月23日 13:18
2018/3/8 追記:
この記事と同じ機能を持つ非同期 HTTP ハンドラを .NET 4.5 から利用できるようになった async / await を利用して実装してみました。その記事は「非同期 HTTP ハンドラ (2)」にありますので興味があれば見てください。この記事の方法が時代遅れなのが分かると思います。

ASP.NET の非同期プログラミングモデルには、(1) 非同期ページ、(2) 非同期 HTTP ハンドラ、(3) 非同期 HTTP モジュールの 3 つがあります。このうち、(2) 非同期 HTTP ハンドラを使って Web サービスのメソッドを非同期に呼び出す場合について、いろいろ不明な点があったので、それらを調べて分かったことを備忘録として書いておきます。

MSDN マガジンの March 2007 の記事「ASP.NET の非同期プログラミングを使ったスケール変換可能なアプリケーション (Wicked Code: Scalable Apps with Asynchronous Programming in ASP.NET)」が、非同期プログラミングの目的、メリット、仕組みなどをサンプルコードを使って詳しく説明しており、大変参考になりました。(2017/5/20 追記:記事は .chm ファイル形式で保存されており、MSDN Magazine Issues and Downloads からダウンロードして読むことができます)

(2015/12/29 追記:非同期プログラミングのメリット、仕組みなど基本的な説明を書いた新しい記事 ASP.NET の非同期/待機の概要を紹介しておきます。.NET 4.5 以降で利用できる async / await を使うことが前提です)

ただし、その中の非同期 HTTP ハンドラのサンプルは複雑すぎて、その仕組みなどがよく分かりませんでした。特に分からなかったのは以下の点です。

  1. Web サービスのメソッドを非同期で呼び出すメソッドをどのように生成するか(例えば、Web サービスのメソッドが HelloWorld だったとすると、BeginHelloWorld と EndHelloWorld をどのように作成するか)。
  2. 非同期 HTTP ハンドラのコードで非同期呼び出しのためのメソッド(上の例で言うと、BeginHelloWorld メソッドと EndHelloWorld メソッド)をどのように呼び出すか。
  3. BeginProcessRequest メソッドの第 2 引数として渡されるコールバックメソッドのデリゲートは何か。
  4. EndProcessRequest メソッドが呼び出される仕組みとそのタイミングはどうなっているか。

という訳で、簡単なサンプルを作って上記の点について調べてみました。下の画像は、説明のためにここに紹介したサンプルを実行したときのもので��。

非同期 HTTP ハンドラの使用

サンプルとして、以下の Web サービスを非同期 HTTP ハンドラを使って利用することを考えます。これに定義されているのは HelloWorld メソッドだけです。同期 HTTP ハンドラではこれを呼べば済みますが、非同期 HTTP ハンドラを使う場合は非同期で HelloWorld メソッドを呼び出し、結果を処理しなければなりません。

非同期呼び出しのために、BeginHelloWorld メソッドと EndHelloWorld メソッドが必要ですが、それらはどのように作ればよいのでしょうか?

(1) Web サービス (141-HelloWorldWebService.asmx)

<%@ WebService Language="C#" 
    Class="_141_HelloWorldWebService" %>

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Threading;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class _141_HelloWorldWebService  : WebService {

  [WebMethod]
  public string HelloWorld(int callDuration) 
  {
    Thread.Sleep(callDuration);
    return String.Format(
      "Hello World from WebService. Call Time: {0}",
      callDuration);
  }    
}

答えはサービスプロキシを定義することです。と言っても、自力でコードを書く必要はなく、Visual Studio を使ってサービス参照を追加するか、または、SDK にある Wsdl.exe ツールを利用してサービスプロキシクラスを自動生成すれば OK です。ここでは、後者の Wsdl.exe ツールを利用した例を書きます。

Wsdl.exe ツールの詳しい使用方法は、MSDN ライブラリの XML Web サービス プロキシの作成 にあります。これを参考に、上記の Web サービスの URL とプロキシのコードの出力先のファイル名をパラメータとして、コマンドラインから Wsdl.exe を実行します。以下の画像のような感じです。

wsdl.exe を使ってプロキシクラスを生成

この結果、上記 (1) の Web サービスからプロキシクラス HelloWorldWebService.cs が自動生成されます。コードは以下のとおりです(一部省略、改行等をしています)。

(2) Web サービスプロキシ (HelloWorldWebService.cs)

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Serialization;

[System.CodeDom.Compiler.GeneratedCodeAttribute(
  "wsdl", "2.0.50727.3038")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(
  Name="_141_HelloWorldWebServiceSoap", 
  Namespace="http://tempuri.org/")]
public partial class _141_HelloWorldWebService : 
  System.Web.Services.Protocols.SoapHttpClientProtocol 
{
  private System.Threading.SendOrPostCallback 
    HelloWorldOperationCompleted;
    
  public _141_HelloWorldWebService() 
  {
    this.Url = 
      "http://msdntestnew/141-HelloWorldWebService.asmx";
  }
    
  public event HelloWorldCompletedEventHandler 
    HelloWorldCompleted;

  [System.Web.Services.Protocols.SoapDocumentMethodAttribute(
    "http://tempuri.org/HelloWorld", 
    RequestNamespace="http://tempuri.org/", 
    ResponseNamespace="http://tempuri.org/", 
    Use=System.Web.Services.Description.SoapBindingUse.Literal,
    ParameterStyle=
      System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
  public string HelloWorld(int callDuration) 
  {
    object[] results = this.Invoke(
        "HelloWorld", new object[] { callDuration });
    return ((string)(results[0]));
  }
    
  public System.IAsyncResult BeginHelloWorld(
    int callDuration, 
    System.AsyncCallback callback, 
    object asyncState) 
  {
    return this.BeginInvoke(
      "HelloWorld", 
      new object[] { callDuration }, 
      callback, 
      asyncState);
  }
    
  public string EndHelloWorld(System.IAsyncResult asyncResult) 
  {
    object[] results = this.EndInvoke(asyncResult);
    return ((string)(results[0]));
  }

  // ・・・中略・・・

}

上に示したプロキシクラスに、SoapHttpClientProtocol.BeginInvoke メソッドSoapHttpClientProtocol.EndInvoke メソッド をラップした BeginHelloWorld メソッドと EndHelloWorld メソッドが生成されているのが分かるでしょうか?

このファイル HelloWorldWebService.cs を、Web アプリケーションのルート直下の App_Code フォルダに置けばプロキシは使用可能になり、それに定義されている BeginHelloWorld メソッドと EndHelloWorld メソッドを利用して、以下のように非同期 HTTP ハンドラを作成できます。

(3) 非同期 HTTP ハンドラ (141-HelloWorldAsyncHandler2.ashx)

<%@ WebHandler Language="C#" 
    Class="_141_HelloWorldAsyncHandler2" %>

using System;
using System.Web;
using System.Threading;

public class _141_HelloWorldAsyncHandler2 : IHttpAsyncHandler
{
  private HttpContext _context;
  private _141_HelloWorldWebService _serviceProxy;
    
  // これが呼び出されることはない。万一呼び出されたら例外
  // をスローして自爆する
  public void ProcessRequest (HttpContext context) 
  {
    throw new InvalidOperationException();
  }
 
  public bool IsReusable 
  {
    get { return false; }
  }

  // まず最初にこのメソッドが呼ばれる。
  // cb には ASP.NET が内部で生成したコールバックメソッド
  // void OnAsyncHandlerCompletion(System.IAsyncResult)
  // のデリゲートが渡される。これが EndProcessRequest メ
  // ソッドを呼び出す。extraData には null が渡される。
  public IAsyncResult BeginProcessRequest(
    HttpContext context,
    AsyncCallback cb,
    Object extraData)
  {
    this._context = context;

    context.Response.Write(
      "<p>BeginProcessRequest:<br />" +
      " IsThreadPoolThread is " +
      Thread.CurrentThread.IsThreadPoolThread +
      "<br />" +
      " ManagedThreadId is " +
      Thread.CurrentThread.ManagedThreadId.ToString() +
      "</p>");

    _serviceProxy = new _141_HelloWorldWebService();

    // 終了すると引数 cb に設定したコールバックデリゲート
    // が起動される。その中で EndProcessRequest メソッドが
    // 呼び出される。
    return _serviceProxy.BeginHelloWorld(3000, cb, null);
  }

  // 引数 ar には BeginProcessRequest メソッドの戻り値であ
  // る IAsyncResult オブジェクトが渡される。
  public void EndProcessRequest(IAsyncResult ar)
  {
    _context.Response.Write(
      "<p>EndProcessRequest:<br />" +
      " IsThreadPoolThread is " +
      Thread.CurrentThread.IsThreadPoolThread +
      "<br />" +
      " ManagedThreadId is " +
      Thread.CurrentThread.ManagedThreadId.ToString() +
      "</p>");

    string result = _serviceProxy.EndHelloWorld(ar);

    _context.Response.Write("<p>" + result + "</p>");
  }
}

非同期 HTTP ハンドラでは、BeginProcessRequest メソッドが最初に実行されます。引数 context には現在の HttpContext オブジェクトへの参照が、cb には ASP.NET が内部で生成したコールバックメソッドのデリゲートが、extraData には null が渡されます。

BeginProcessRequest メソッドでは、プロキシクラスを初期化して BeginHelloWorld メソッドを呼び出し、HelloWorld メソッドの非同期実行を開始します。非同期実行の開始後すぐに制御が戻って、ここまでの処理に使用していたスレッドはスレッドプールに戻されます。

BeginHelloWorld メソッドの第 1 引数は HelloWorld メソッドに渡されます(ここでは、待機時間 3000 即ち 3 秒を渡しています)。第 2 引数 cb には BeginProcessRequest メソッドの引数 cb に渡されたコールバックメソッドのデリゲートがそのままコピーされます。第 3 引数は BeginProcessRequest メソッドの戻り値、即ち EndProcessRequest メソッドの引数 ar に渡される IAsyncResult オブジェクトから、AsyncState プロパティを使って取得できるデータですが、ここでは使用しないので null としています。

ここで覚えておくべきことは、BeginHelloWorld メソッドの第 2 引数として渡されたコールバックメソッドのデリゲートが実行されると EndProcessRequest メソッドが実行されることです(逆に言えば、コールバックメソッドが実行されないとフリーズしてしまう)。

非同期処理の完了後、コールバックメソッドのデリゲートが実行され、EndProcessRequest メソッドが呼び出されます。その際、スレッドプールから空いているスレッドを取得して EndProcessRequest メソッドを実行します。この時、引数 ar には BeginProcessRequest メソッドの戻り値である IAsyncResult オブジェクトが渡されます。 EndProcessRequest メソッドの中で、EndHelloWorld メソッドを実行し、Web サービスからの応答をその戻り値から取得します。EndProcessRequest メソッドの終了後、制御がコールバックメソッドに戻って全体のタスクが終了します。

上記の非同期 HTTP ハンドラを iframe の src 属性に設定した aspx ページの例は以下の通りです。一番上の画像は、以下の aspx ページの実行結果です。

(4) aspx ページ (141-HelloWorldAsyncHandler.aspx)

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
    <iframe src="141-HelloWorldAsyncHandler2.ashx" 
      id="iframe2"
      width="400px" 
      height="200px">
    </iframe>
  </form>
</body>
</html>

Tags:

ASP.NET

ASP.NET AJAX でページの静的メソッド呼び出し

by WebSurfer 2012年8月18日 16:32

ASP.NET の AJAX 機能を使って、ブラウザからクライアントスクリプトでサーバー側のメソッドを呼び出し、データを受け取って表示する方法を、先の記事 ASP.NET AJAX と Web サービス で紹介しました。

その例では、呼び出す相手は Web サービス (.asmx) のメソッドでしたが、それ以外に、自分のページ (.aspx) の静的メソッドを呼び出すことも可能です。

そのためのポイントは、簡単に書くと以下の 3 点です。

  1. ScriptManager で EnablePageMethods を true に設定。
  2. クライアントスクリプトから呼び出すサーバー側のメソッドは public static にして WebMethodAttribute 属性を付与。
  3. クライアントスクリプトからは、プロキシクラス PageMethods を通じて呼び出し。プロキシクラス PageMethods のコードはサーバーで自動生成され、html ソースにインラインで含まれてブラウザに送信されます。(呼び出し先が Web サービスの場合は WebService.asmx/js というような外部ファイルになります)

詳しくは、MSDN ライブラリの クライアント スクリプトへの Web サービスの公開 の「ASP.NET Web ページ内の静的メソッドの呼び出し」というセクションを参照してください。

サンプルコードは上に紹介した MSDN ライブラリのページにもありますが、先の記事 ASP.NET AJAX と Web サービス で書いた Web サービスの例を、Page の静的メソッドを使う方法に置き換えたサンプルコードを以下に書いておきます。

(1) Web ページ (173-ASPNETAjaxAndStaticMethod.aspx)

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Linq" %>
<%@ Import Namespace="System.Web.Services" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    public class Car
    {
        public string Make;
        public string Model;
        public int Year;
        public int Doors;
        public string Colour;
        public float Price;
    }

    static protected List<Car> Cars = new List<Car>{
        new Car{Make="Audi",Model="A4",Year=1995,
            Doors=5,Colour="Red",Price=2995f},
        new Car{Make="Ford",Model="Focus",Year=2002,
            Doors=5,Colour="Black",Price=3250f},
        new Car{Make="BMW",Model="5 Series",Year=2006,
            Doors=4,Colour="Grey",Price=24950f},
        new Car{Make="Renault",Model="Laguna",Year=2000,
            Doors=5,Colour="Red",Price=3995f},
        new Car{Make="Toyota",Model="Previa",Year=1998,
            Doors=5,Colour="Green",Price=2695f},
        new Car{Make="Mini",Model="Cooper",Year=2005,
            Doors=2,Colour="Grey",Price=9850f},
        new Car{Make="Mazda",Model="MX 5",Year=2003,
            Doors=2,Colour="Silver",Price=6995f},
        new Car{Make="Ford",Model="Fiesta",Year=2004,
            Doors=3,Colour="Red",Price=3759f},
        new Car{Make="Honda",Model="Accord",Year=1997,
            Doors=4,Colour="Silver",Price=1995f}
    };

    [WebMethod]
    public static List<Car> GetCarsByDoors(int doors)
    {
        var query = from c in Cars
                    where c.Doors == doors
                    select c;

        return query.ToList();
    }    
    
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>ASP.NET AJAX to call static method</title>
  <script src="Scripts/jquery-1.4.1.js" type="text/javascript">
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" 
      runat="server" 
      EnablePageMethods="True">
      <Scripts>
        <asp:ScriptReference 
          Path="~/173-ASPNETAjaxAndStaticMethod.js" />
      </Scripts>
    </asp:ScriptManager>
    <div>
      Number of doors: 
      <asp:DropDownList ID="ddlDoors" runat="server">
        <asp:ListItem>2</asp:ListItem> 
        <asp:ListItem>3</asp:ListItem>
        <asp:ListItem>4</asp:ListItem>
        <asp:ListItem>5</asp:ListItem>
      </asp:DropDownList>   
    </div>
    <input 
      type="button" 
      id="Button1" 
      value="Get Cars" 
      onclick="getCars($('#<%=ddlDoors.ClientID%>').val());" /> 
    <div id="output"></div>
  </form>
</body>
</html>

(2) JavaScript (173-ASPNETAjaxAndStaticMethod.js)

var serviceProxy;

// プロキシの初期化
function pageLoad() {
    serviceProxy = new PageMethods();
}

// ボタンクリックで呼び出されるメソッド  
function getCars(doors) {
    serviceProxy.GetCarsByDoors(doors, Succeeded, Failed);
}

// AJAX 通信が成功したときに呼び出され、戻ってきたデータ
// を処置するコールバック関数。
// 引数 cars は JSON 文字列ではなく、パース済みのオブジェ
// クト。.NET 3.5 で追加された d パラメータはプロキシで
// 適切に処置されるらしい。
function Succeeded(cars) {
    $('#output').empty();
    $.each(cars, function (index, car) {
        $('#output').append(
            '<p><strong>' + car.Make + ' ' +
            car.Model + '</strong><br /> Year: ' +
            car.Year + '<br />Doors: ' +
            car.Doors + '<br />Colour: ' +
            car.Colour + '<br />Price: £' +
            car.Price + '</p>');
    });
}

// 通信に失敗したとき呼び出されるコールバック関数。 
function Failed(error, userContext, methodName) {
    if (error !== null) {
        var msg = "An error occurred: " +
            error.get_message();
        $('#output').text(msg);
    }
}

if (typeof (Sys) !== "undefined") {
    Sys.Application.notifyScriptLoaded();
}

なお、上記 Web ページの静的メソッド GetCarsByDoors は jQuery.ajax からも呼び出せます。先の記事 jQuery AJAX と Web サービス で紹介したコードで、呼び出し先 (url) を変更するだけです。呼び出し先の Web ページの URL が上の例のように 173-ASPNETAjaxAndStaticMethod.aspx とすると、以下のような感じです。

// 前略 
function getCars() {
  $.ajax({
    type: "POST",
    url: "173-ASPNETAjaxAndStaticMethod.aspx/GetCarsByDoors",
    data: "{doors: " + $('#ddlDoors').val() + "}",
// 後略

その場合、上で述べた ScriptManager は不要ですし、プロキシにアクセスするスクリプト(上の例で言うと 173-ASPNETAjaxAndStaticMethod.js ファイル)も不要です。それゆえ、呼び出し元は .aspx ページである必要はなく、静的な .html ページとしても OK です。

Tags:

AJAX

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar