.net MVCのサーバをちょっと変えたら403で動かなくなった。調べてみると、web.configに以下を入れるといいらしい。
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <handlers> <remove name="UrlRoutingHandler"/> </handlers> </system.webServer>
これでとりあえず動いた。参考にしたのはココ。
プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。
.net MVCのサーバをちょっと変えたら403で動かなくなった。調べてみると、web.configに以下を入れるといいらしい。
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <handlers> <remove name="UrlRoutingHandler"/> </handlers> </system.webServer>
これでとりあえず動いた。参考にしたのはココ。
EF使う際に、POCO的なオブジェクトでテーブルの定義を作っておく。↓の感じ
[Table("HOGETABLE")] public class TestTable { [Key] public int HOGETABLEID {get;set;} public string 何か { get; set; } }
テーブル名指定には↑の感じで属性で定義しておく。
で、EFじゃなくて、独自にクエリとか作ったりしたい場合に、これの属性のテーブル名が欲しかったりする。その取り方。
public class TestModel : DbContext { public DbSet<TestTable> TestTables { get; set; } } string TableName = ""; using (var context = new TestModel()) { foreach (var tmp in context.TestTables.GetType().CustomAttributes) { if (tmp.AttributeType.Name == "TableAttribute")//テーブル名を入れてるとこ { TableName = tmp.ConstructorArguments[0].Value.ToString(); break; } } }
EntityFrameworkを.net MVCで使う場合、Web.Configに↓の感じで接続文字列を設定しておく。
<connectionStrings> <add name="DefaultConnection" connectionString="Data Source=***********;Initial Catalog=HOGEDB;Persist Security Info=True; User ID=sa;Password=xxxxxx" providerName="System.Data.SqlClient" /> </connectionStrings>
で、EFは使い勝手はそこそこいいんだけど、個別にDBにつないで自分でやりたい場合もある。その場合に、この接続文字列を取得したい。やり方は色々あると思うけど、Web。Configの中身を直で持ってくるやり方。
string wkStr = ""; for (int i = 0; i < System.Configuration.ConfigurationManager.ConnectionStrings.Count; i++) { if (System.Configuration.ConfigurationManager.ConnectionStrings[i].Name == "DefaultConnection") { wkStr = System.Configuration.ConfigurationManager.ConnectionStrings[i].ConnectionString; break; } }
設定が複数ある場合もあるので、接続名指定して取得する。色々試してみて、これしかやりようなかった。他に何かいい方法ないかしら。。。
「System.Configuration.ConfigurationManager.ConnectionStringst」ってforeach出来ないのね。Whereとかも使えない。IEnumにどーにかできないものだろうか。
.net MVCで独自にアクセスログを取りたくなった。どこに入れるのが正解なのかよくわからない。 何となくGlobal.asaxにリクエスト来たときに書けばいいのかと思い、↓でとってみる。
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { //アプリの起動時の処理 } //↓これで書く protected void Application_OnBeginRequest(object sender, EventArgs e) { // HTTP リクエスト処理開始時の処理 WriteLog();//ログをはく } }
「Application_OnBeginRequest()」だとリクエスト来た時に全部拾えるからここがいいかなーと思ったけど、 User.Identityとゆーか認証の状態がとれかった。まぁ当たり前ですか。。。
認証後の状態はいつかなーで見てたら⇒でした。「Application_OnAuthenticateRequest()」これの中に書けばUser.Identityとかちゃんと取れる。ログイン前はNullってるけども。
リクエストのライフサイクル的な話はココで見れる。いろんなブログとかもあるけど、やっぱまず、MSDN見たほうがいいと思う。
で、とりあえず、アクセスログっぽいものはこれで作れたんだけど、なんとなく、ほんとにココでいいのか自信が持てない。動作上は特に問題無いよう思えるんだけど。。。
あと、ログ取ってみて思ったけど、やっぱSignalRはすげーコネクション使うのね。なんかこんなに接続しちゃってよいのだろーかと不安になるくらいつなぐ。最近のWebはこんなもんなのだろうか。。。
.net MVCでSignalRを使う。ちょっとはまったりもしたのでメモメモ。
とりあえず導入は、Nugetで「SignalR」をインストール。そしたらStartupのConfigurationしてる奴に以下を追加。
public void Configuration(IAppBuilder app) { app.MapSignalR();//←これを追加 }
そしたら、Hubを作る。どこでもいいっぽいけど、Hubsってフォルダを作ってやった場合。
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; namespace Hoge.Hubs { [HubName("hogehub")] public class HogeHub : Hub { public void Send(string text) { Clients.All.HogeShortInfo(text); } } }
次はView側。↓の感じ。コメント送信したら皆に飛ぶ感じのよくあるやつ。
<div> <input type="text" value="" id="message"/><input type="button" id="sendBtn" value="Send"/> </div> <div id="messageDiv"> </div> <script src="~/Scripts/jquery.signalR-2.2.0.min.js"> <script src="/signalr/hubs"> <script type="text/javascript"> $(function () { var echo = $.connection.hogehub; echo.on("HogeShortInfo", function (text) { $('#messageDiv').append(text); }); $.connection.hub.start().done(function () { $('#sendBtn').click(function () { var msg = $('#message').val(); echo.server.send(msg).done(function () { $('#message').val(""); }); }); }); }); </script>
一個はまったのが、「signalr/hubs」。この子は動的に作られるらしく探しても出てこない。 ってとこまではいいんだけど、作られる階層がサイトのトップらしく、少し階層いったところのページでやる場合は「src="/signalr/hubs"」って頭に"/"をつけておかないと読み込んでくれない。大部分のサンプルは"signalr/hubs"ってなってるのでちょっとはまる。。。
ついで、小文字と大文字。基本的に小文字しか使えないと思ってたほうがいいのかしら。。。
で、書き方は何種類かあるっぽい。クライアント側のメソッドを定義するのは↓の2つの感じ。
echo.on("HogeShortInfo", function (text) { $('#messageDiv').append(text); }); echo.client.HogeShortInfo= function (text) { $('#messageDiv').append(text); });
個人的には上の書き方の方が、割り当ててますって感じがして好き。
あと、$.connection.hub.start().done()の中にイベントハンドラ入れとかないと、 接続する前にボタンとか押されてダメになるらしい。試してないけど作った人がそう言ってんだからそうしておきましょう。
って、ここまでは割と見かける。このやり方はチャットみたいな送信って押してみんなにいくみたいな場合。そーじゃなくて、サーバ側から何らかのイベント時に(Hubの外から)全体に送信したい場合はクライアントの呼び出しを下の感じで書く。
var hubContext = GlobalHost.ConnectionManager.GetHubContext<Hubs.HogeHub>(); hubContext.Clients.All.HogeShortInfo("なんか周知");
HogeShortInfoのところは、クライアントで割り当てた関数名を指定する感じ。↑のやつはコントローラーでもモデル側でも入れられるけど、↓のusingが必要。
using Microsoft.AspNet.SignalR;
で、サーバ側から任意のタイミングでクライアントの画面にメッセージ表示するみたいなのが↑ので出来るようになるんだけど、クライアント側のJavaScriptでもちょっとはまった。
$(function () { var echo = $.connection.hogehub; echo.on("HogeShortInfo", function (text) { $('#messageDiv').append(text); }); $.connection.hub.start();//←これが必須!!! });
「$.connection.hub.start();」これを入れないと動かない。
あと、サーバから返すデータは普通にJsonっぽいので↓の感じのオブジェクト作って返してあげてクライアント側で使ったりしやすい。
[System.Runtime.Serialization.DataContract] public class TestHubJsonData { [System.Runtime.Serialization.DataMember()] public int recid = 0; [System.Runtime.Serialization.DataMember()] public string state = ""; [System.Runtime.Serialization.DataMember()] public string date = ""; }
下の感じで使用。オブジェクトの配列で返してもよきかな。
echo.on("TestDisp", function (data) { alert(data.date); })
C#からMongoDBを触るのを試してる。何となく検索して出てくる日本語の情報が古い気がする。 MongoDB.DriverはNuGetで取れる「2.2.3」を使用。まずは、基本的なとろこから。
//接続文字列(MongoDBをインストールしてデフォルトのままの場合) string connectionString = "mongodb://localhost"; //MongoDBに接続 MongoClient client = new MongoClient(connectionString); //データベースを選ぶ(use foo) var database = client.GetDatabase("foo"); //コレクションを決める var collection = database.GetCollection("bar"); //とりあえず最初の一件を取得() var finddocument = collection.Find(new BsonDocument()).FirstOrDefault();
↓ためし用のクラス。
public class Heroes { public ObjectId Id { get; set; } public string ヒーロー { get; set; } public string 所属 { get; set; } }
CRUD
public void TestCRUD() { //接続文字列(MongoDBをインストールしてデフォルトのままの場合) string connectionString = "mongodb://localhost"; //MongoDBに接続 MongoClient client = new MongoClient(connectionString); //データベースを選ぶ(use foo) var database = client.GetDatabase("foo"); //コレクションを決める var collection = database.GetCollection("heroes"); ///////////////////////////////////////////////////////////// //1件作成 var oneDocument = new Heroes { ヒーロー = "ヤン",所属="同盟" }; collection.InsertOne(oneDocument); ///////////////////////////////////////////////////////////// //複数件作成 List documents = new List (); documents.Add(new Heroes { ヒーロー = "ラインハルト", 所属 = "帝国" }); documents.Add(new Heroes { ヒーロー = "メルカッツ", 所属 = "帝国" }); documents.Add(new Heroes { ヒーロー = "キルヒアイス", 所属 = "帝国" }); collection.InsertMany(documents); ///////////////////////////////////////////////////////////// //Find var filterbuilder = Builders .Filter; var filter = filterbuilder.Eq("所属", "同盟"); var findDocuments = collection.Find (filter); foreach (var tmp in findDocuments.ToList()) { Console.WriteLine(tmp.ヒーロー); } //結果 ⇒ ヤン ///////////////////////////////////////////////////////////// //更新 var updatefilter = filterbuilder.Eq("ヒーロー", "メルカッツ"); var updateElem = Builders .Update.Set("所属", "同盟"); var result = collection.UpdateMany(updatefilter, updateElem);//マッチした中の先頭1件のみ削除の場合はUpdateOne if (result.IsModifiedCountAvailable) { Console.WriteLine("【更新結果】マッチ:{0}件 更新:{1}件", result.MatchedCount, result.ModifiedCount); } findDocuments = collection.Find (filter); foreach (var tmp in findDocuments.ToList()) { Console.WriteLine(tmp.ヒーロー); } //結果 ⇒ ヤン メルカッツ ///////////////////////////////////////////////////////////// //削除 var delfilter = Builders .Filter.Eq("ヒーロー", "ヤン"); var delresult = collection.DeleteMany(delfilter);//マッチした中の先頭1件のみ削除の場合はDeleteOne Console.WriteLine("削除件数:{0}", delresult.DeletedCount); findDocuments = collection.Find (filter); foreach (var tmp in findDocuments.ToList()) { Console.WriteLine(tmp.ヒーロー); } //結果 ⇒ メルカッツ }
試してみて、分かった内容は次。
とりあえず、ObjectIDは必須ですね。クラス定義で使う場合は無いとまず動かない。 あとはデータ作った後に、プロパティ追加してもFind出来た。追加したプロパティがフィールドに無い場合はもちろんNullでかえってくるけども。クラスの拡張はいけそー。コードファースト的なノリでいけそー。ただ、やっぱプロパティの削除はダメね。まぁ、当たり前か。
で、見かけるサンプルって、こんな感じでクラス定義して使うのがほとんど。
でもそれって、
スキーマレスじゃないと思うんだ。
MongoDBの利点なのかどーかは色々あるだろーけどもスキーマレスなのが特徴だったよーな。
とゆーわけでスキーマレスにCRUDしましょ。まぁ、公式のQuickTourがスキーマレスでやってるからそれを見ろってことなのかしら。
次は、スキーマレスの操作と、性能回りの実験やる。
複数のプロジェクトに分けて.Net MVCのアプリを作りたい場合にとりあえずできたこと。
ある程度、大きくなる想定のWebアプリを作る場合に、単一のプロジェクトで構成するとえらいこっちゃになる。 とにかく、プロジェクト分けたいのです。一発プロジェクトは漢らしくていいんだけど、複数チームで作業する場合は機能とかもろもろ考慮で分業しておきたいの。。。
複数のプロジェクトで一個のWebアプリにしたい場合にどうやるかを試したメモ。 わりと当たり前のように必要な事だと思うのですが、調べても調べてもドンピシャの情報に いきあたらないのはなんでなんでしょ。この程度は当たり前の事なのかな・・・。
とりあえず、サンプルとして以下の感じにする。調べてみると、一つのソリューションの中に複数のプロジェクトがある感じのものは見るけど、ソリューションごと別の奴は見たことないのでソリューションも分ける。
MainWeb : 大本のプロジェクト SubWeb : 分割しておくプロジェクト
まず、MainWebのプロジェクトを作る。とにかく簡素な感じにしたいので、空MVCで作る。↓の感じ
そしたら、コントローラーとViewを適当に追加しておく。 出来たら、「Areas」っていうフォルダをプロジェクト直下に追加しておく。とりあえず↓の感じ。
SubWebも同じ感じで作る。↓の感じ。
SubWebの直下にAreaRegistrationのクラスを作る。↓の感じ。
ファイルの名前(クラスの名前)は何でもいいっぽいけど、「SubWebAreaRegistration」にしておいた。 中身は以下にして、SubWebをビルドしておく。
using System.Web.Mvc; namespace SubWeb { public class SubWebAreaRegistration : AreaRegistration { public override string AreaName { get { return "SubWeb"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "SubWeb_default", "SubWeb/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } } }
次はMainWeb側の参照にSubWeb.dllへの参照を追加して、ビルドしておく。
そしたら、IISの設定をする。まず、新しくサイトを作るで、MainWebでサイトを作っておく。
出来たら、中のフォルダの「Areas」を右クリして、仮想ディレクトリの追加でSubWebを追加する。
これで、とりあえず動くようになる。サイトのアクセスは下の感じ。
これで、プロジェクトを分割したMVCの作りが出来そう。まだいろいろ試せてないけど、 基本はこれでいいのだろうか・・・。いまいち分かってない。
ついでに、今回は仮想ディレクトリで持ってきたけど、MainWebのViewsの中にcshtmlだけコピーしても普通に動く。仮想ディレクトリを追加する前に、SubWebにアクセスしようとすると、cshtmlとかの探してる順番がエラーログに出るので参考に見てみてもいいかも。。。
結局のところは、ルーティングの設定をどうするかっていう事なんだろうか。 単一プロジェクトだと、「Global.asax」の「RouteConfig.RegisterRoutes(RouteTable.Routes);」ってところで、「RegisterRoutes」呼び出してやってるけど、Areaを使う感じにすると、「Global.asax」の「AreaRegistration.RegisterAllAreas();」って呼び出しのところでAreaRegistrationを全部やるみたいな動きになる模様。
で、「RegisterAllAreas()」の中で、参照に登録さているアセンブリの中から「AreaRegistration」を継承して作ってあるクラス(今回の例だと「SubWebAreaRegistration」)を全部探し出して勝手にやってくれるみたい。
次は認証とDB回りをこの構成で試す。あと、ルートの書き方は多分足りてない。要調整。
なんか作ったはいいが、別の端末で動かそうとすると、バージョンが入ってねーって怒られる場合の対処。もちろん.Net4.0以降でしか入ってない機能を.Net2.0で動かそうとするのは無理。
コンフィグにアプリがサポートするバージョンを明示しておかないとダメらしい。↓の感じ。
<configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> <supportedRuntime version="v2.0.50727" sku=".NETFramework,Version=v2.0.50727"/> </startup> </configuration>
VisualStudioで言うと、「App1.config」って感じのファイルがあればそれ。なければ追加で「アプリケーション構成ファイル」を追加する。実際には「実行ファイル名.exe.config」みたいなファイルがそれ。
MSDN的には
このアプリケーションがサポートする共通言語ランタイム (CLR: Common Language Runtime) のバージョンを指定する文字列値。 CLR の最初の 3 つのバージョンは、"v1.0.3705"、"v1.1.4322"、 および "v2.0.50727" で指定します。 .NET Framework 4 以降では、必要となるのはメジャー バージョン番号と マイナー バージョン番号のみです (つまり、"v4.0.30319" ではなく "v4.0")。 短い文字列を使用することをお勧めします。 .NET Framework バージョン 3.0 および 3.5 では CLR のバージョン 2.0.50727 が使用されます。
だそうですよ。
APIを作ったはいいがPOSTの各データがなんか取れない場合に、とりあえず受け取る方法。 いろいろ試してこれしか無かった。絶対、他に原因があるはず。。。
System.Web.HttpContext.Current.Request.Form["name"].ToString()
.netMVCでApiController継承してるコントローラで Server.MapPathが使いたくなった時。
System.Web.Hosting.HostingEnvironment.MapPath("/hoge/moe");