LoginSignup
13

More than 5 years have passed since last update.

Monoならではの問題と対応方法

Last updated at Posted at 2017-04-21

はじめに

Mono 好きです!

初めて Mono を試したとき、Windowsで発行した ASP.NET MVC アプリが Linuxで動いたのは、魔法かと思っちゃいました。
そんなステキな Mono ですが、やっぱりちょっとだけ気をつけることがあります。
モノズキな方へのTip集。

筆者の環境は下記のとおりになっています。

  • Visual Studio 2015
  • .NET Framework 4.5.1
  • ASP.NET MVC 5
  • Mono 4.6.2
  • XSP 4.2.2
  • fast-cgi-mono-server 4.2.2 (0.4.0.0)
  • Debian 8.6
  • Nginx 1.6.2

Machine Key

フォーム認証や暗号化のために必要な Machine Key。
普通(Windows IIS) は、Web.config に明記していなければ自動的に生成されるらしいんだけど、Mono じゃダメです。
サービスを再起動する度に値が変わってしまうため、System.Web.Security.MachineKey.Protect などでの暗号した値を decodeするときに例外を吐いてしまいます。

なのでWeb.configに明記します。
Generate ASP.NET Machine Keys
↑のサイトで、Generate Keyボタンを押下して、生成された文字列を、<System.Web>空間内にコピペすればOKです。

<machineKey validationKey="E4C13FB44CF8CBCD126896B77B0A38D05D7290A47ADB1D248500A624D4A07607D096402371E15D8F981D8071986B72FEBDE40248A63DA764F48F7996F24C7871" decryptionKey="AB4996AF83FEB94C5018CC7FD003F3909C553DCEAA5FEE4AC7C6432DE3DFD701" validation="SHA1" decryption="AES">

Client Validation

Model のアノテーション設定から Validation を行ってくれる便利なアレ。
NuGet から、パッケージをちゃんと入れて、Web.config の ClientValidationEnabled も true にして、IIS環境ではちゃんと動きましたーー
でも、Monoでは、Client Validation のための HTML タグを吐いてくれない・・・そんなとき。

<input data-val="true" data-val-required="メールアドレス は入力必須項目です。" id="MailAddress" name="MailAddress" type="text" value="" />

<input data-val="true" data-val-required="パスワード は入力必須項目です。" id="Passwd" name="Passwd" type="password" value="" />

ほんとうなら、↑こんなHTMLを出力してくれるハズが、
↓のようなプレーンがタグになっちゃう。

<input id="MailAddress" name="MailAddress" type="text" value="" />

<input id="Passwd" name="Passwd" type="password" value="" />

これの解決方法は、次項にて。

1発目のリクエストで例外発生

サービスを再起動したり、プログラムを入れ替えたりした後の、最初のリクエストを行うと、System.UnauthorizedAccessException を吐くことがあります。
(F5押下してリロードすると、動きだす)

image

これは、Mono 用に一時ディレクトリを作ってやれば、解決します。
ディレクトリの名前は、例外メッセージに表示されています。
このディレクトリを、所有者をサービスを実行しているユーザにして作ってやればOKです。
Mono をパッケージからインストールしたときは、/etc/mono/registry
ソースから入れたときは、/usr/local/etc/mono/registry
fast-cgiを使っているなら、fast-cgi の実行ユーザ
Apache + mod_mono なら、Apache の実行ユーザ

また、Webサーバのエラーログを見てみると、
"実行ユーザのホーム ディレクトリ/.mono" がdenidだぞ・・・
とあるので、こちらのディレクトリも同じように作成しておきます。

こちらの設定を行うと、前項の「Client Validation」の問題も解決します
ちゃんと validation のダグが生成されていました!

Knockout の ViewModel を JSON にシリアライズするとき

Knockout を使った Formデータを POSTした時に Internal error: key is null, empty or not a string.てな例外が出たら、JSONをうまくデシリアライズできないのが原因のようです。
Windows環境では、問題なく動くのに、Mono だとダメなときがあります。

どうやら、ko.mapping を使って ViewModel 化したものを、ko.toJSON でシリアライズすると、余計なプロパティ(__ko_mapping__)が付いたままになり、これが付くと、Mono 上ではモデルバインダーが例外を吐いてしまうようです。

ko.mappingを使ったものは、必ず「ko.mapping.toJSON」でシリアライズするように、気をつける必要があります。

ClosedXMLでのExcelが破損?

サーバサイドで Excelファイルを生成する処理があったので、最初は ClosedXMLを使ってました。
ところが、Mono 環境で実行すると、生成したExcelファイルを開くときに、↓のエラーが出てしまいます。
image

image

毎回、最初の問いに「はい」とすれば、Excelが開くんだけど、どうやらこれは xlsx 形式の Zip 圧縮で微妙な差異があるらしいのが原因みたいです。
※WindowsBase.dll を参照に入れたり、Web.config でアセンブリを明示してもダメでした。

仕方ないので、ClosedXML の利用を諦め、EPPlusというパッケージを試してみたら、問題なく開くことが出来ました。
EPPlus は、OpenXML SDK を使わずに Open Office XML を生成するので、単純な表なら十分だし、実効速度も速いです。

NLog.Web 設定で aspnet 変数を書くと例外

NLog の拡張ライブラリ「NLog.Web」を入れると、設定ファイルの記述だけで ASP.NET の環境変数をログに出力できます。

<!-- layout のところで、リクエストURLをログに出力しています -->
<target name="accessLog"
        xsi:type="File"
        layout="${longdate} ${aspnet-request:serverVariable=Url} - ${message} ${exception:format=tostring}"
        fileName="${logDirectory}/access.log"
        archiveEvery="Day"
        archiveFileName="${logDirectory}/access.{#}.log"
        archiveNumbering="Date"
        archiveDateFormat="yyyyMMdd"
        encoding="utf-8"
        maxArchiveFiles="30" />

でもこれを Mono で実行すると
Non-web exception. Exception origin (name of application or object): System.Web.Mvc.
てな例外が出てしまいます。

仕方ないので、出力したい情報(リクエストURL)は、ログ内容文字列に含めることにしました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13