HTTP Error 500.19 - error code:0x8007000d の解決方法
前回記事と同様、WindowsPCでIIS7.5を初起動させようとして起こる、よくあるらしい現象。
StackOverFlowの書き込みによれば、前回同様、基本的にWeb Platform Installerに起因する現象のようです。
エラーメッセージにはApplicationHost.configが原因みたいな事が書かれていますが、実際にはApplicationHost.configを編集する必要はなく、Web Platform Installerを使って「url rewrite」をインストールすることで解決できました。
私なりに原因と解決方法をまとめると、以下の通りです。
原因
Web Platform Installerを使ってASP.NETをインストールすると発生する
解決方法
Web Platform Installerを使ってurl rewriteをインストールする
HTTP Error 500.21 - error code:0x8007000d の解決方法
ローカル環境に新たにIISをインストールして実行したところ、表題の通りのエラーが発生し、解決できたのでメモ。
環境
- OS: Windows7 Pro 64bit(VirtualBox内)
- IIS: 7.5
- .NET framework: ver4.0
症状
サイト全体において表題のエラーが発生。
HTTP Error | 500.21 |
---|---|
Error Code | 0x8007000d |
Message | ASP.NETがインストールされていないか、完全にインストールされていません(再現できなかったので覚えているメッセージです。少し違うと思います) |
原因
エラーメッセージは「ASP.NETがインストールされていない」旨の内容ですが、正確にはIIS側にASP.NETを登録(?)するプロセスがされていないことが原因のようです。Web Platform Installerを使って.net frameworkをインストールするとこの処理がされないような情報も以前見た気がしますが、定かではありません。
解決方法
HTTP Error 500.21 when trying to host an ASP.NET web app with IIS 7 のWinAnimesh氏のReplyの通りですが、詳しく手順を示します。
LayoutTemplate内にコードブロック
LayoutTemplate内にコードブロックは配置できない
ListViewのLayoutTemplate内にコードブロックが配置できない事、および代替策をメモします。
例えば以下のように、何らかの一覧をulでバインドし、上部に件数を示したいケースを想定します(該当件数の箇所だけListView外に置けばいいだろう、という突っ込みはナシです)。
<asp:ListView id="lv" ...>
<LayoutTemplate>
該当件数: <%=_listCount %>
<ul>
<asp:PlaceHolder ID="itemPlaceholder ... />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li><%# Eval("Name") %></li>
</ItemTemplate>
</asp:ListView>
コードビハインドは以下の通り。
protected _listCount = 0;
protected Page_Load(...) {
MyCompany mycom = new MyCompany();
List<Employee> employees = mycom.GetEmployee();
_listCount = employees.Count;
lv.DataSource = employees;
lv.DataBind();
}
これを実行すると、以下の様なエラーになります。
コントロールにコード ブロック (<% ... %>) が含まれているため、コントロールのコレクションを変更できません。
どうやらLayoutTemplate内でコードブロックは使えない事がわかります。
<%# ~ ->にすれば…
次に、「=」を「#」に変えてみました(以下、LayoutTemplate部のみ抜粋)。データバインドをしているので、何となくイケそうな気がしますが…
<LayoutTemplate>
該当件数: <%# _listCount %>
<ul>
<asp:PlaceHolder ID="itemPlaceholder ... />
</ul>
</LayoutTemplate>
結果は、エラーこそ表示されないものの、件数の部分が空欄になってしまいます。
該当件数: ←何も表示されない?!
・田中
・鈴木
・山田
以上の現象から、このListViewの挙動に関して、2つの原因が考えられます。
- ListViewのデータバインドはあくまでItemTemplateやGroupTemplateに対してのみであり、LayoutTemplate部は対象外
- コードビハインドの実行よりも、LayoutTemplate部のデータバインドが先に実行されてしまっている
ListView_LayoutCreatedイベントで解決
どちらが正しいか分かりませんが、今回もStackOverFlowの過去の書き込みで解決しました。
asp.net - Get public string in codebehind into LayoutTemplate of ListView - Stack Overflow
の中でReplyされている、以下の箇所です。
protected void lv_LayoutCreated(Object sender, EventArgs e)
{
lv.Controls[0].DataBind();
}
どうやらControls[0]がLayoutTemplateを示すらしく、これは手動でバインドしてやらなると良いようです。
そもそもLayoutTemplateがバインドを自ら行わないから強制的にバインドさせてやる必要があるのか、それともバインドがコードビハインドより先に行われてしまう為に後から再度実行させてやる必要があるのか判然としませんが、とりあえず解決策でした。
オブジェクトの権限一覧、ストアドプロシージャの引数一覧
権限一覧はSQL Server Management Studioで確認できるわけですが、確認のためだけにSSMSを起動するのは重いし、管理者権限であれば操作ミスも起こりえます。
日頃Common SQL Environmentを使用しているのであれば、コマンドで実行できれば簡単だと考え探したところ、発見出来ました。
ASP.NETのPostback先URLに、Rewrite前のアドレスを設定したい
ASP.NETでURLの置き換えをする場合、通常Rewriteを使用します。
URLの置換え規則は正規表現で定義しますが、この辺りは他のサイト等でも多く書かれているので割愛します。
ここでは、ASP.NETのRewriteを使う際によく問題になる「Postback先URL」に触れたいと思います。
例えばクライアント側から以下のアドレスへの要求があったとします。
/site1/product/city-13101/price-980/ (BEFOREとします)
もちろんこれは仮想アドレスに過ぎず、実体は存在しません。Rewriteにより以下の実ページヘアクセスされます。
/site1/product.aspx?city=13101&price=980 (AFTERとします)
この場合、クライアントから見えているURLはBEFOREですが、ASP.NETとしてはAFTERとして認識されるため、Postback先URL(formタグのaction要素)も必然的にAFTERのアドレスが入ります。その結果、ここでSubmit等によりPostbackが発生すると、クライアントからAFTERのアドレスが丸見えになってしまうのです。
そこで、HTTP_REFERRERを使ってPostback先のURLを強引に書き換えてしまうのが、以下のコードです。
protected void Page_Load(object sender, EventArgs e) { form1.Action = Request.RawUrl; }
▼参考
ASP.NET postbacks creates problem in URL rewriting?
以前はMasterPageにformを設置した時期もありましたが、こういうのを見るとやはりページ単位にformを置くのが良いと感じますね。
IIS7.0で.net framework4.0で動作させる
BugTrackerを導入しましたが、.net framework 4.0で動作するようでした。
これまで全て2.0ベースで開発してきたため、4.0は初めてでしたが、VSの開始オプションを「4.0」にすれば良いだけだと軽く考えていました。が、実際にはIIS側の設定が必要でした。
症状から説明します。BugTrackerを導入後に実行したところ、
認識されない属性 'targetFramework' です。
というエラー画面になり、web.configの「
エラー画面に共通している、画面下に表示される実行時の.netのバージョンが2.0.xxxとなっており、4.0で実行されていないことが分かります。
ASP.NETサイトを .NET Framework 4環境へ移行すると「構成エラー:認識されない属性 'targetframework' です」エラーが発生する
にもある通り、アプリケーションプール(以降、APとします)側の変更なようで、手順は以下の通り。
- IIS Managerを開く
- 左サイドより「アプリケーションプール」
- .net4にするサイト名を右クリックして「基本設定」
- 「.net framework version」が2になっているので、4に変更
以上で完了したと思いましたが、私の場合この状態だとまだver2.0で実行されていました。
私の場合、サイトアプリケーションのAPが「Default Application Pool1」(だったかな)になっており、サイト名と同じAPになっていなかったためです。
必ずしも同じようになるわけではないと思いますが、もしAPが「Default〜」になってしまっていた場合の方法は以下の通り。
- アプリケーションを選択して「基本設定」
- アプリケーションプールを「Default〜」からサイト名に変更
以上。
HtmlAgilityPackの基本
HTMLからスクレイピングでデータを抜き出す際に使用しました。
現時点でわかった使用例を示します。
HTML code example
<html> <head></head> <body> <h3>企業いろいろ</h3> <h4>コンビニ</h4> <ul> <li><a href="711.htm">セブン-イレブン</a></li> <li><a href="lawson.htm">ローソン</a></li> <li><a href="famima.htm">ファミリーマート</a></li> </ul> <h4>PC</h4> <ul> <li class="usa"><a href="hp.htm">HP</a></li> <li class="usa"><a href="dell.htm">Dell</a></li> <li><a href="epson.htm">Epson Direct</a></li> </ul> <h4>EC</h4> <ul> <li class="usa"><a href="amazon.htm">Amazon</a></li> <li><a href="rakuten.htm">楽天</a></li> </ul> <div class="migi">注意:順不同です</div> <div id="footer">copyright 2013 Kobarin</div> </body> </html>
前処理(対象によって変更して下さい)
string html = ""; //HTMLコード HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.OptionAutoCloseOnEnd = false; //最後に自動で閉じる(?) doc.OptionCheckSyntax = false; //文法チェック。 doc.OptionFixNestedTags = true; //閉じタグが欠如している場合の処理 doc.LoadHtml(html);
取得コード
Exp1. id・classを指定しての要素を取得(単一取得)
HtmlAgilityPack.HtmlNode node1 = doc.DocumentNode.SelectSingleNode("div[@id='footer']"); Console.WriteLine(node1.InnerHtml);
結果
copyright 2013 Kobarin
Exp2. <li>タグの中身を全部取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
<a href="711.htm">セブン-イレブン</a> <a href="lawson.htm">ローソン</a> <a href="famima.htm">ファミリーマート</a> <a href="hp.htm">HP</a> <a href="dell.htm">Dell</a> <a href="epson.htm">Epson Direct</a> <a href="amazon.htm">Amazon</a> <a href="rakuten.htm">楽天</a> <a href="yahoo.htm">ヤフーショッピング</a>
Exp3. aタグの文字列部分を取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
セブン-イレブン ローソン ファミリーマート HP Dell Epson Direct Amazon 楽天 ヤフーショッピング
Exp4. 特定classのタグのみ取得
//<li class="usa">のタグを取得 HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[@class='usa']"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
<a href="hp.htm">HP</a> <a href="dell.htm">Dell</a> <a href="amazon.htm">Amazon</a>
Exp5. 順番を指定して特定タグを取得
//body直下にある、2番目のdivタグ HtmlAgilityPack.HtmlNode node = doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]"); Console.WriteLine(node.InnerHtml);
結果
copyright 2013 Kobarin
Exp6. aタグのhref内のURLを全て取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a"); foreach (HtmlAgilityPack.HtmlNode node in nodes) { string url = node.GetAttributeValue("href", ""); Console.WriteLine(url); }
結果
711.htm lawson.htm famima.htm hp.htm dell.htm epson.htm amazon.htm rakuten.htm yahoo.htm
Exp7. 全ul内の1番目のli内のリンク文字列だけ取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[1]/a"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
セブン-イレブン HP Amazon
Exp8. 全ul内の最後のli内のリンク文字列を取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[last()]/a"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
ファミリーマート Epson Direct 楽天
Exp9. 各ul内で2番目以降にあり、且つclassが"usa"のli内にあるリンク文字列を取得
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//li[position() >= 2 and contains(@class, 'usa')]/a"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
Dell
Exp10. a内のテキストに'D'を含む内容を取得(※カンマを使います)
HtmlAgilityPack.HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a[contains(.,'D')]"); foreach(HtmlAgilityPack.HtmlNode node in nodes){ Console.WriteLine(node.InnerHtml); }
結果
Dell Epson Direct
注意点
HTMLコードそのものにエラーがあると正常に処理されません。
例えば私が試した所では、閉じタグが抜けていたり、<div><p></div></p>のように互い違いになっていると抽出も出来ないため、ソースコードの段階で補正をしてやる必要があります。
参考
HtmlAgilityPackの記述は原則的にXPathに準拠するらしく、自分の場合はXPath Syntaxを参考にさせて頂きました。
また、HtmlAgilityPack Simulatorも公開しましたので、記述テスト等にお使い下さい。