読者です 読者をやめる 読者になる 読者になる

だるろぐ

とてもだるだるした日記です http://about.daruyanagi.jp/

お知らせ

WebMatrix:フィードを読み込むときに System.Xml.XmlException が発生する

WebMatrix ASP.net Web Pages

ASP.NET Web Pages 製の BOT が、ある日を境に突然、System.Xml.XmlException エラーを吐くようになった。

The element with name 'RDF' and namespace 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' is not an allowed feed format.

某サイトのリニューアルで、RSS 2.0 が RSS 1.0 になったせいでフィードが読めなくなったらしい/(^o^)\

Classes That Model Syndication Feeds and Items

  • SyndicationFeed - represents a syndication feed. Has properties like Title, Description, Links, and Items. The Items property represents the collection of content items expressed in the feed.
  • SyndicationItem - represents a specific syndication feed item and includes properties like Title, Summary, PublishDate, Authors, and so on.

Classes That Transform Syndication Feeds To/From XML

  • Rss20FeedFormatter - can take a SyndicationFeed object and turn it into XML that conforms to the RSS 2.0 specification. Also, can be used to consume a properly-formatted RSS 2.0 feed, turning the XML into a SyndicationFeed object with its properties set based on the data in the consumed XML.
  • Atom10FeedFormatter - same as the Rss20FeedFormatter, but uses the Atom 1.0 standard.
Syndicating and Consuming RSS 1.0 (RDF) Feeds in ASP.NET 3.5 - 4GuysFromRolla.com

Rss10FeedFormatter は標準で用意されていないみたいなので、自分で実装する必要がある。

幸い、コードが GitHub に転がっていたので拝借(見覚えのあるアイコンの人だなぁ……)。おおきにさんきゅーやで!

f:id:daruyanagi:20160603190721p:plain

これを以下のように呼び出してやれば、解決。めでたしめでたし。

try
{
    feed = SyndicationFeed.Load(reader);
}
catch
{
    // エラーがでたら RSS 1.0 ってことにしておく
    var formatter = new Rss10FeedFormatter();
    formatter.ReadFrom(reader);
    feed = formatter.Feed;
}

WebMatrix:改修工廠の早見表がほしかったので作ってみた

艦これ WebMatrix ASP.net Web Pages

f:id:daruyanagi:20150919201617p:plain

今週は超忙しい&体調がよろしくなかった“ので”、合間を見つけて前々からほしかった改修工廠の早見表を作ってみた。接続詞が間違っているというツッコミが入りそうだけど、こういうのって、そういうときにこそやりたくならない?

最初に作ったヤツ

f:id:daruyanagi:20150919201737p:plain

ASP.NET Web Pages + SQL Server Compact で作ってみたけど、だいぶ遅かった。遅いのは多分、自分が SQL わかってないせいだと思う(SELECT でごっそりとってきたデータを LINQ で加工するとかいうわけのわからないことをやっていた)。SQL が分かんないのは一朝一夕に改善できないので、とりあえずデータベースへの問い合わせ結果をキャッシュしまくってみたけれど、スタートアップの遅さは改善できない。Web サイトがスタンバイ(スリープ? なに?)したら、キャッシュを一から再構築しなきゃいけないのも困った。

――というわけで、廃棄する予定。

二番目に作ったヤツ

f:id:daruyanagi:20150919202146p:plain

で、ちょっと考えてみたんだけど、こういうデータっていうのは稀にあるサーバーのメンテナンスのときにしか更新されないわけだ。だったら、なにもデータベースに入れる必要はなかった(CURD のうち R しかやらへんやん?)。JSON か何かでデータを用意しておいて、サーバーのスタートアップ時に読み込めばいい。

俺ってアホだなーと思いながら、ぐちゃぐちゃっと今日半日で作ったら(ロジック組むより、データ打ち込む方がよっぽど時間かかった)、最初に作ったヤツよりだいぶ早くて満足。コードを整理しながらロジック見直せばもうちょっと早くなりそう。なんせ今のは cshtml に foreach が腐るほど埋まってるという正真正銘のクソコードだし……ちょうどいいから今度 GitHub にあげて、GitHub Flow ってのを勉強するネタにしようと思う。

ほんとうはこれを Windows Phone に移植しておでコンに出したかったけれど、いつの間にか締め切り過ぎてたみたい。残念。艦これ Android 版が出るらしいし、Android アプリにするのもアリかなぁ? 

WebMatrix: URL にドットを含めたい

WebMatrix ASP.net Web Pages

f:id:daruyanagi:20150913032211p:plain

Wiki エンジンなんかを作るときなど、「ドット(.)」を URL に含めたい場合は、Web.config に一行、以下のように加えるといいみたい。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
    </system.webServer>
</configuration>

静的リソースまでアプリケーションで処理するようになる(?)ので、効率が悪そうだけど。もっとちゃんと対策したければ、以下の URL が参考になりそう。

「Visual Studio Community」で ASP.NET Web Pages をはじめる

ASP.NET Web Pages

f:id:daruyanagi:20150705212532p:plain

みんなも大好きな「WebMatrix」ですが、そろそろ本格的に“なかったこと”になっているような気がしてならない今日この頃。代わりとなるのはおそらく「Visual Studio Code」なのでしょうが、まだちょっとベータ版なので、今回は定評のありまくる「Visual Studio Community」で ASP.NET Web Pages をはじめる方法を調べてみました。「WebMatrix」で“空のサイト”に相当するものを作るのが今回の目標です。

とりあえずプロジェクトを作成する

f:id:daruyanagi:20150705213348p:plain

まずはプロジェクトを作成する必要があります。プロジェクトの新規作成ダイアログで[Templates]-[Visual C#]-[Web]-[Visual Studio 2012]とツリーを辿り、「ASP.NET Empty Web Application」を選択します。Visual Basic じゃないと死んでしまう病に冒されている人は、[Visual C#]の代わりに[Visual Basic]を選択するとよいでしょう。

f:id:daruyanagi:20150705213553p:plain

ちなみに、[Templates]-[Visual C#]-[Web]にある「ASP.NET Web Application」テンプレートを使っても構いません。

f:id:daruyanagi:20150705213747p:plain

このテンプレートを選択すると、プロジェクトの種類なんかを選べるダイアログが現れるので、ここで「Empty」を選択すればさっきと同じ結果になると思います(厳密にくらべたわけじゃないから知らんけど)。このダイアログからプロジェクトを作ると Azure Web Sites も同時に作れるので、必要な場合はこっちを選ぶと手間が省けるかも。

ページの追加

f:id:daruyanagi:20150705214139p:plain

プロジェクトを作成したら、ページの追加。コンテストメニューを開いて[Add]-[MVC 5 View Page (Razor)]を選択します。

f:id:daruyanagi:20150705214317p:plain

あとは名前を付けると、cshtml ファイルが生成されます。ASP.NET(Web Pages)の流儀にしたがって、最初のファイルの名前は Default.cshtml にしておきましょうか。中身はこんな感じです。

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title></title>
</head>
<body>
    <div>
    
    </div>
</body>
</html>

body が空っぽなので、ついでにこんな感じでサンプルコードを加えておきます。

@{
    Layout = null;
    var message = "Hello! World"; // 追加
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title></title>
</head>
<body>
    <div>
        @message <!-- 追加 -->
    </div>
</body>
</html>

これで[F5]キーを押せば、"Hello! World" と書かれたサイトが立ち上がるはず――

f:id:daruyanagi:20150705214739p:plain

――だったのですが、ダメでした。http://go.microsoft.com/fwlink/?LinkId=254126 を読めと言われるのでそれに目を通してみますと、Web.config で利用する ASP.NET Web Pages のバージョンを指定しろと書いてあります。仕方ないので、言われたとおりにキーを追加。

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>

  <appSettings> <!-- このセクションを追加 -->
    <add key="webPages:Version" value="2.0" />
  </appSettings>
</configuration>

f:id:daruyanagi:20150705215117p:plain

すると、サイトが立ち上がりました! めでたしめでたし! あとは SQL CE データベースを読み書きできるようにしたり、その中身を見るためのよさげなツールを探したり(クッソめんどくせー!)、追加の NuGet パッケージを入れたりするだけですね。これで WebMatrix を窓からポイできます。スターターサイトなんかは、Yeoman かなにかで準備する感じになるのかなぁ……ちょっとずつ新しい流儀に慣れていかないとね。

おまけ

ASP.NET Web Pages は .NET 言語で PHP っぽく Web ページのロジックを記述できるフレームワークです。PHP よりも安全に作られているので、お仕事で使わなければならないかわいそうな人でもなければ、こっちの方がお勧めデス。

書き方は @shibayan のサイトでも参考にしてください。PHP の記法と比べながら簡単に解説してくれています。

「(1)」って書いてあるのに続編がないのは気にしないでおきましょう。あとは

なんかが詳しいかもしれません。

WebMatrix:フィードを読み込んでデータベースへつっこむときに System.Data.SqlServerCe.SqlCeException が発生する。

WebMatrix ASP.net Web Pages SQL CE

だいぶ悩んで、Visual Studio まで立ち上げていろいろデバッグしていたのだけど、原因は簡単だった。

datetime

300 分の 1 秒、つまり 3.33 ミリ秒の精度で、1753 年 1 月 1 日から 9999 年 12 月 31 日までの日付と時刻のデータを格納するデータ型です。値は .000、.003、または .007 ミリ秒の単位になるように丸められます。

2 つの 4 バイト整数として格納されます。最初の 4 バイトは、base date である 1900 年 1 月 1 日からの日数、またはこの日までの日数を格納します。基準日とは、システムが参照する日付です。datetime 型の値には、1753 年 1 月 1 日より前の日付を使用できません。もう一方の 4 バイトは、午前 0 時から数えた時間をミリ秒単位で格納します。秒の有効範囲は 0 から 59 までです。

データ型

f:id:daruyanagi:20150625045042p:plain

一方、C# の default(DateTime)0001/01/01 0:00:00。つまり、SyndicationFeed オブジェクトの LastUpdateTime プロパティあたりにちゃんと値がセットされてなくて(そういうフィードを配信しているサイトは割とある)、default(DateTime) を返してくるとき、それをそのまま SQL CE データベースに格納しようとするとエラーになる。

f:id:daruyanagi:20150625050249p:plain

SQL CE の datetime 型を扱う場合は、事前に値の範囲に収まるかチェックして、ダメな場合はハネておかないといけないんだね。

var min_date = new DateTime(1753, 1, 1); // ほんとは readonly でどこかに
var updated_at = item.LastUpdatedTime.UtcDateTime < min_date 
    ? new DateTimeOffset(DateTime.Now).UtcDateTime 
    : item.LastUpdatedTime.UtcDateTime;

// Database.Execute();

教訓

ちゃんとマニュアルは読もう。

WebMatrix 3:oEmbed ヘルパーを作ってみた(2)

WebMatrix ASP.net Web Pages

WebMatrix 3:oEmbed ヘルパーを作ってみた - だるろぐ の続き。今回は Flickr の埋め込みをやってみようかと思う。

~/App_Code/OEmbed.cshtml

@helper Flickr(string url) {
    const string API_ENDPOINT = "http://www.flickr.com/services/oembed/";

    using (var downloader = new WebClient())
    {
        try
        {
            // URL を組み立てて JSON の oEmbed データを取得
            var request = string.Format("{0}?url={1}&format={2}", API_ENDPOINT, url, "json");
            var oembed_data = downloader.DownloadString(request);
            var oembed_json = Json.Decode(oembed_data);

            @ObjectInfo.Print(oembed_json) // デバッグのため

            var embed_type = oembed_json.type as string;

            switch (embed_type) // photo と video の二種類がある
            {
                case "photo":
<figure>
    <img src='@oembed_json.url' alt='@oembed_json.title'>
    <figcaption>
        <img src='http://favicon.qfor.info/f/@oembed_json.provider_url' />
        <a href='@oembed_json.web_page'>@oembed_json.title</a>
        by <a href='@oembed_json.author_url'>@oembed_json.author_name</a>
    </figcaption>
</figure>
                    break;
                case "video":
<figure>
    @Html.Raw(oembed_json.html)
</figure>
                    break;
                default:
                    break;

            }
        }
        catch (Exception exception)
        {
            <p class='error'>@url: @exception.Message</p>
        }
    }
}

~/Default.cshtml

<!DOCTYPE html>

<html lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta charset="utf-8" />
        <title>マイ サイトのタイトル</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
    </head>
    <body>
        @OEmbed.Flickr("https://www.flickr.com/photos/daruyanagi/6219334657/")
        @OEmbed.Flickr("https://www.flickr.com/photos/dmartinie/5760711397/")
    </body>
</html>

結果

たとえば Photo の場合。Twitter のときみたいに html を返してくれないので、自分で組み立てる。

f:id:daruyanagi:20140812194342p:plain

<figure>
    <img src='https://farm7.staticflickr.com/6162/6219334657_ba91a4498d_z.jpg' alt='SMSはやく使えるようになりたい'>
    <figcaption>
        <img src='http://favicon.qfor.info/f/https://www.flickr.com/' />
        <a href='https://www.flickr.com/photos/daruyanagi/6219334657/'>SMSはやく使えるようになりたい</a>
        by <a href='https://www.flickr.com/photos/daruyanagi/'>daruyanagi</a>
    </figcaption>
</figure>

Video の場合。これは html があるのでそれを使う。

f:id:daruyanagi:20140812194506p:plain

<figure>
    <object type="application/x-shockwave-flash" width="500" height="281" data="https://www.flickr.com/apps/video/stewart.swf?v=145061" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> <param name="flashvars" value="intl_lang=en-us&photo_secret=5f7c3bff83&photo_id=5760711397&flickr_show_info_box=true"></param> <param name="movie" value="https://www.flickr.com/apps/video/stewart.swf?v=145061"></param> <param name="bgcolor" value="#000000"></param> <param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="https://www.flickr.com/apps/video/stewart.swf?v=145061" bgcolor="#000000" allowfullscreen="true" flashvars="intl_lang=en-us&photo_secret=5f7c3bff83&photo_id=5760711397&flickr_show_info_box=true" height="281" width="500"></embed></object>
</figure>

f:id:daruyanagi:20140812194912p:plain

写真の方は CSS でいい感じにデコってみた。なかなかいいかも。

WebMatrix:Web.config の appSettings を使いこなす

WebMatrix ASP.net Web Pages

WebMatrix:ローカルとリモートで異なる設定を利用する方法を考えてみた - だるろぐ でちょっと興味がわいたので、Web.config について少しいろいろ試してみた。

基本

f:id:daruyanagi:20140717005913p:plain

Default.cshtml

<!DOCTYPE html>

@using System.Configuration

<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>マイ サイトのタイトル</title>
    </head>
    <body>
        @ObjectInfo.Print(ConfigurationManager.AppSettings)
    </body>
</html>

Web.config の appSettings セクションに記述したアプリケーション設定を読み込む。

@ObjectInfo.Print は WebMatrix でちょっとしたデバッグをするときに便利なのでぜひ覚えておこう。

Web.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>

    <appSettings file="Web2.config">
        <add key="A" value="a"/>
        <add key="B" value="b"/>
        <add key="C" value="c"/>
        <add key="D" value="d"/>
        <add key="E" value="e"/>
        <add key="F" value="f"/>
        <add key="G" value="g"/>
    </appSettings>
</configuration>

appSettings セクションにアプリケーション設定を記述。file="Web2.config" は後述。

結果

f:id:daruyanagi:20140717011748p:plain

たとえば、

var a = System.Configuration.ConfigurationManager.AppSettings["A"];

などとすることで、Web.config の appSettings セクションに記述したアプリケーション設定が得られる。

ちょっとめんどくさいけれど、API キーなどの静的値はなるべくハードコードせず、appSettings に書いておくべき。

外部 .config ファイルを利用したアプリケーション設定のオーバーライド

f:id:daruyanagi:20140717011257p:plain

以下のような Web2.config ファイルを追加。

Web2.config

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <add key="E" value="1"/>
    <add key="F" value="2"/>
    <add key="G" value="3"/>
</appSettings>

結果

f:id:daruyanagi:20140717010123p:plain

ConfigurationManager.AppSettings["E"] 以降の値が Web2.config の内容によって書き換えられる。

Web.config の file 属性で指定した外部 .config ファイルが存在しない場合は、読み込み処理がスキップされる。

外部 .config ファイルを利用したアプリケーション設定の強制削除

ちょっと Web2.config をイジってみる。

Web2.config

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <clear /> // <- 追加
    <add key="E" value="1"/>
    <add key="F" value="2"/>
    <add key="G" value="3"/>
</appSettings>

結果

f:id:daruyanagi:20140717010556p:plain

Web.config で記述したアプリケーション設定がクリアされる。

要するに、ASP.NET は .config を読んで、appSettings セクションに書かれた add や clear といったコマンドを AppSettings(Dictionary 型)に対して行っているだけ。

なので、

<?xml version="1.0" encoding="utf-8"?>

<appSettings>
    <remove key="A" />
</appSettings>

とすれば、特定のキーだけを消すことも可能。これを使えば、Web.config で行ったアプリケーション設定を Web2.config から自由にイジれる。使い方間違うとハマるかもだけど。

外部 .config ファイルを隠しファイルにする

結論を先に言うと、Web2.config を隠しファイルにしても、appSettings はちゃんと読み込まれる。

なので WebMatrix:特定のファイルを発行対象に含まない - だるろぐ と組み合わせることで、

  • Web.config
  • Web2.config(隠しファイル、リモートに発行されない)

という構成にすれば、

  • ローカル:Web.config + Web2.config のアプリケーション設定で動作
  • リモート:Web.config のアプリケーション設定で動作

という風に運用できる(WebMatrix:ローカルとリモートで異なる設定を利用する方法を考えてみた - だるろぐ は発想を逆にすればよかった!)。