SPAじゃないReactを.Net Coreで扱う
ちょっと特殊なReactの使い方を説明します。MVCで部分的にJqueryを使っていることが多いと思いますが、そのJqueryで作った部分がReactに置き換わる形です。ちなみに業務でAngularは触っているのですが、Reactは触っていませんので超ド素人です(通勤で調べて今試した感じ(笑))。なので、Reactの説明と言うよりは、.Net Coreのテンプレートをどう改造すれば部分コンポーネントとして扱えるかって記事になります。
なぜReactなのか?
ReactはViewに特化したフレームワークなので手軽に始められそうっていう浅はかな考えがきっかけ。フルスタックなAngularと違って最低限なライブラリしか用意されてないらしいので、無理にJSで作ろうと無茶な開発をしないっても大きいです。あとReact Nativeがあるから学習するなら一番コスパ高いです。
フルスタックの方が良いのでは?
個人的な意見ですが、今の段階でフルスタックなJSを使ってWEBサービスは作らない方が良いと思います。Angularなんか割となんでもできるので、サーバーサイドで作るかフロントで作るか結構曖昧になってしまってサーバーでもフロントでも同じ処理があるじゃんってオチになりがちな印象があります。さらにコンポーネントを分けたはいいものの、結局Input、Output、ViewChild、Subjectなど多用してわけわからなくなる状況に陥りがちです(前の現場はヒドかった。。)時間と意欲があればなんでも良いと思いますけどね。
で本題ですが、SPAじゃないReactとは以下の画像のような感じをイメージしてもらえればと思います。
緑がMVCで出力しているViewで水色、ピンクがReactのそれぞれのコンポーネントになっています。SNSだったらいいね機能、コメント機能でコンポーネントを分けられるので、綺麗に作れます。昔JqueryでSNSチックなので作ったのですが、メンテしづらいコードになっちゃいました。。これをJSフレームワークで作れたら結構綺麗になっただろうなーと思います。このサイトです(宣伝)
作り方
dotnet coreのテンプレートを使用しますので、以下のコマンドを入力します。特にdotnetに限ったことじゃないですが圧倒的に楽です。
dotnet new react -o {ディレクトリ名}
次にNodeのパッケージをインストールしてきます。
npm i
これでテンプレートは動くはずです。
boot.tsxを改造する
テンプレートだと以下のコードになっているはずです。
import "./css/site.css"; import "bootstrap"; import * as React from "react"; import * as ReactDOM from "react-dom"; import { AppContainer } from "react-hot-loader"; import { BrowserRouter } from "react-router-dom"; import * as RoutesModule from "./routes"; let routes = RoutesModule.routes; function renderApp() { // This code starts up the React app when it runs in a browser. It sets up the routing // configuration and injects the app into a DOM element. const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href")!; ReactDOM.render( <AppContainer> <BrowserRouter children={ routes } basename={ baseUrl } /> </AppContainer>, document.getElementById("react-app") ); } renderApp(); // Allow Hot Module Replacement if (module.hot) { module.hot.accept("./routes", () => { routes = require<typeof RoutesModule>("./routes").routes; renderApp(); }); }
これはid="react-app"と記載されているところに表示されます。SPA用のマウントなのでルーティング用のパラメータが指定されてます。このReactコンポーネントのマウントをベタがきします。
import "./css/site.css"; import "bootstrap"; import * as React from "react"; import * as ReactDOM from "react-dom"; import { Counter } from "../ClientApp/components/Counter"; import { FetchData } from "../ClientApp/components/FetchData"; function renderApp(): void { // これ ReactDOM.render(<Counter />,document.getElementById("counter-app")); ReactDOM.render(<FetchData />,document.getElementById("fetch-app")); } renderApp();
上記のコードを噛み砕きます。
まずはコンポーネントをimportします。
import { Counter } from "../ClientApp/components/Counter"; import { FetchData } from "../ClientApp/components/FetchData";
次にコンポーネントをマウントします。
ReactDOM.render(<Counter />,document.getElementById("counter-app")); ReactDOM.render(<FetchData />,document.getElementById("fetch-app"));
これでdocument.getElementByIdで指定したタグにコンポーネントが紐づきます。Railsだとreact-railsっていうgemがあるみたいなんだけどnugetにはないのかな?
次にコンポーネント側の修正します。
以下のコードを修正します。
export class Counter extends React.Component<RouteComponentProps<{}>, CounterState> { // ↓に変更する export class Counter extends React.Component<{},any> {
テンプレートのままだとboot.tsxでエラーになりますので「RouteComponentProps<{}>, CounterState」を排除しましょう。Stateはコンポーネント内で定義すればいらないので排除しちゃって良いっぽいです(正直よくわかってない)
これでマウントされたコンポーネントがつかえるようになります。簡単でしたが意外と探しても少なかったので調べました。このソースコードはgithubにおいたので興味ある方は試してみてください。これからReactバリバリ使う予定なので、もっと理解深めてブログに書いてきます。次は.NetCore MVCにReactを入れるミドルウェアとかの紹介かな<さっそくReactじゃない
github.com
ちなみに
.Net CoreのテンプレートだとTypeScriptがReactでもついてる!やば最高。
"typescript": "2.4.1",
参考
非SPAなサービスにReactを導入する - クックパッド開発者ブログ
【react】jqueryメインの非SPAシステムの特定ページでのみreactを導入してみたのでメモ - とりあえずphpとか
Macで作ったASP.NET MVC CoreのDocker ImageをHerokuで動かす【想像編】
タイトル長杉
最近Azureにハマってます。今回のタイトルはAzureで出来ますし楽ですしやる意味わからんと思う人がいると思いますが、それなりに理由があります。
なぜにへろく?
SSLを使った画面が必要になったためです。
で、SSL化するためにはインフラの選定が必要です。ほぼ稼働しないと考えて良いサイトなので、お金がかからないってのが一番重要でHerokuはHerokuドメインならSSLが付いてきます。AzureもSSLなんですが無料は1日60分です、アホ。有料だと最低でも7000円です、アホ。
いくつか候補をあげました。
・そのままxdomainでSSL化する
ない。絶対にない。有料だし意味わかんない。あ、ちなみにxdomainのPHPサーバで運用してるしょぼアプリです。
・Azure、GCP、AWSのいずれかを使う
あり。でもほとんど稼働しないサイトにお金を使う意味があるのか?
・railsで作ってHerokuにあげる
これかなり有力。今朝までこれで考えてたのでrailsを調べてました笑
・Core MVCのDocker ImageをHerokuで動かす
絶対これ。.Net CoreとDockerとHerokuって言うパワーワードにやられました。
Herokuは月550時間まで無料なので運用費0です。って言うことでDockerで作ってみます。
が
まだ実際に作ってないので想像編です。多少調べたんで作り方を書いて、実際試したときの違いなどを違う記事で書ければと思います。
OSはMacです。WindowsのDocker Imageはてんでダメで動かんとの記事を見たのでこれが良いでしょう。
Docker for Macを入れる
正しくインストールされているか確認する
docker version
HomebrewでHeroku CLI をインストール
brew install heroku/brew/heroku
dotnet new mvc -o contact
いつも思うけど-oオプションが指定された記事少ない!わからんでプロジェクト作成するとカレントに作られてぐしゃぐしゃになるんでちゃんと指定する例があった方が良いと思うの。
プロジェクト発行
dotnet publish -c Release
Dockerファイル作成
FROM microsoft/aspnetcore
WORKDIR /app
COPY . .
CMD ASPNETCORE_URLS=http://*:$PORT dotnet contact.dll
Dockerイメージ作成
docker build contact .
Heroku用のタグ付け
docker tag contact registry.heroku.com/<Heroku アプリ名>/web
デプロイ
docker push registry.heroku.com/<Heroku アプリ名>/web
これで動くのは実証されてるんですが、メールとか動くんかな?まぁ多分詰まるのはここじゃない感じがするけど
<追記>
実際にデプロイまでできたのですが、反映されません。もしかして.net core 2.1だと出来ない!?.net core2.0の記事をみてたから
違いがそこらぐらいしかわからない(´・ω・`)困った。
参考
Deploy asp.net core 2.0 apps on Heroku – Devcenter Square Blog
ASP.NET Core MVC アプリケーションの Docker コンテナを Heroku で動かしてみた - present
正式版の.NET Core 2.1がリリースされました。
.NET Core 2.1の正式版が5/30にリリースされました。
github.com
そこまで感動する変更はないのですが、まぁ良い感じだったのでリリースノートに添って書いてきます。
Linuxインストーラの変更とディストリビューションの更新
Debian系のOSでパッケージマネージャのアップデート(apt-get update)がサポートされるようです。以下のようなコマンドがいらなくなる感じだとおもう。
wget -q packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
サポート範囲狭杉
.NET Core Tools
いまいちよくわからないけど、以下のコマンドが使えるようになったらしい。
dotnet tool
dotnet build プロセス制御
ちょっと便利。
ビルドサーバープロセスを手動で終了。これCtrl+Cでできなかったっけ?
dotnet build-server shutdown
ワーカープロセスが作成されないようにする。地味に残るから結構よさそう。
dotnet build -nodeReuse:false
ネットワークパフォーマンス
プロキシを設定するHttpClientHandlerに代わってよりパフォーマンスの良いものがリリースされたそうです。
あまりここら辺理解できてないので割愛します。
APIも2.0から結構変わってます。
github.com
余裕があれば確実にバージョンを上げたほうが良いね!
ASP.NET Core MVCのエラーハンドリング【起動時編】
前回の記事の続きです。やっとみんなの洋楽ランキングの.NET Core対応が終わりましたので今週末に完全移行します!.Net CoreなのでAzureからGCPに行こうしようと思いましたがやっぱりAzure良い!特に不満がなければAzureで行こうと思ってます。近いうちにLinuxサーバーに切り替える予定。
tekitoumemo.hatenablog.com
話がそれましたが、「ASP.NET Core MVCのエラーハンドリング【起動時編】」です。
前回はあくまでMVCのエラーハンドリングなので、Startup.csやProgram.csのエラーが取れません。なので、起動時のエラーを取る必要があります。ということで公式を見てみます。
アプリの起動中に起こる例外はホスティング層だけが処理できます。 Web ホストを使うと、captureStartupErrors と detailedErrors キーを利用して、起動中のエラーに対するホストの動作を構成できます。
はい。captureStartupErrors と detailedErrorsを使えばなんとかなるらしいので試してみます。
CaptureStartupErrorsを扱う
Program.csのBuildWebHostに設定します。
public static IWebHost BuildWebHostDevelopment (string[] args) => WebHost.CreateDefaultBuilder (args) .UseStartup<Startup> () .CaptureStartupErrors (true) // これ .Build ();
Startup.csにエラーを仕込んで実行します。そうすると見慣れたエラー画面が出てきます。
Startupのエラーをキャッチするもののようです。本番でこんなの使えませんのでDevelopmentモードで使いましょう。
detailedErrorsを扱う
同じくProgram.csのBuildWebHostに設定します。
public static IWebHost BuildWebHostDevelopment (string[] args) => WebHost.CreateDefaultBuilder (args) .UseStartup<Startup> () .UseSetting ("detailedErrors", "true") // これ .Build ();
同じくStartup.csにエラーを仕込んで実行します。そうするとWEBサーバーが立ち上がることなく終了します。よくわからないのでググるとIISのオプションでスタックトレースを返すオプションだそうです。
Controls whether a stack trace is returned when there is a fault generated from the Web service.
<detailedErrors> Element
こちらは本番のエラーハンドリングに使えそうです。
Production用とDevelopment 用のWeb ホストを用意する
CaptureStartupErrorsはDevelopmentに、detailedErrorsはProductionにとそれぞれWEBホストを用意します。
// Production public static IWebHost BuildWebHosProduction (string[] args) => WebHost.CreateDefaultBuilder (args) .UseStartup<Startup> () .UseSetting ("detailedErrors", "true") .Build (); // Development public static IWebHost BuildWebHostDevelopment (string[] args) => WebHost.CreateDefaultBuilder (args) .UseStartup<Startup> () .CaptureStartupErrors (true) .Build (); }
これで環境によってエラーハンドリングの準備が整いました。次に切り替えを作成します。
環境によってWEBホストを切り替える
上記の例からProductionはBuildWebHosProduction、DevelopmentはBuildWebHostDevelopmentが実行されるよう以下のように記述します。
public static void Main (string[] args) { if (Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT") == "Development") { BuildWebHostDevelopment (args).Run (); } else { BuildWebHosProduction (args).Run (); } }
Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT") で実行モードを取得できるので文字列によって、実行するWEBホストを切り替えます。最後に本番用にエラーを取得する処理を記述します。
起動時のエラーを取得する
Startup.csにtry、catchを記載します。正直これが綺麗なのかいまだに謎ですが、StackOverflowにそのやり方が書いてあったので信じましょう。
public static void Main (string[] args) { try { if (Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT") == "Development") { BuildWebHostDevelopment (args).Run (); } else { BuildWebHosProduction (args).Run (); } } catch (Exception ex) { var mailManager = new MailManager { Subject = "[みんなの洋楽ランキング]エラー報告", Body = string.Format ("{0}\n{1}", ex.Message, ex.StackTrace) }; mailManager.SendEmailAsync ().Wait (); } }
Developmentモードの場合はCaptureStartupErrorsがエラーをキャッチするので、tryで捕捉されずにブラウザに出力されます。Productionの場合はtryで捕捉し、メールを飛ばす仕様になっています。以下の例ではserilogの捕捉方法ですが、やりたいことはだいたい一緒です。C#の7.1からMainにasyncが使えるようですが、終了するだけなのでWaitを使ってます(MVCでもasyncが使えるのか不明)
github.com
アプリケーション全体にtry catchステートメントを追加することを躊躇しています
と言ってますので、やっぱりなんとも言えないやり方かもしれませんがエラーが取れるのでよしとしましょう。
ざっとですが、これでMVCアプリケーションのエラーが取れました。起動時のエラーハンドリングはなかなか定まってなさそうですが、状況にあった取得方法を考えていければと思います。
検索順位が1ページ目まで上がってきた!検索ボリュームがなかなか多いキーワードなので嬉しい!!ここらへんも何をやってるか随時ブログにあげていきます。
ASP.NET Core MVCのエラーハンドリング【MVC編】
みんなの洋楽ランキングにて.Net Coreのエラーハンドリングを実装する必要がありました。エラーハンドリングのやり方は公式でも載ってるのですが、実用的な記事があまりないので僕なりのやり方をここで紹介しようと思います ※これは私のオリジナールも含まれているのであくまで参考程度に
例外フィルター
MVC5でもあったOnExceptionを使用します。MVC5ではフィルターじゃなくても実装出来ましたが公式がフィルターの例を出しているのでその例を以下に記載します。
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute { private readonly IHostingEnvironment _hostingEnvironment; private readonly IModelMetadataProvider _modelMetadataProvider; public CustomExceptionFilterAttribute( IHostingEnvironment hostingEnvironment, IModelMetadataProvider modelMetadataProvider) { _hostingEnvironment = hostingEnvironment; _modelMetadataProvider = modelMetadataProvider; } public override void OnException(ExceptionContext context) { if (!_hostingEnvironment.IsDevelopment()) { // do nothing return; } var result = new ViewResult {ViewName = "CustomError"}; result.ViewData = new ViewDataDictionary(_modelMetadataProvider,context.ModelState); result.ViewData.Add("Exception", context.Exception); // TODO: Pass additional detailed data via ViewData context.Result = result; } }
MVC5とほぼ一緒です。_hostingEnvironment.IsDevelopment()はDevelopモードの場合はなにもしないという意味です。私の場合、こんなにいろいろ必要なかったのでめちゃくちゃシンプルにしました。
public override async void OnException (ExceptionContext context) { var mailManager = new MailManager { Subject = "[みんなの洋楽ランキング]エラー報告", Body = string.Format ("{0}\n{1}", context.Exception, context.Exception.StackTrace) }; await mailManager.SendEmailAsync (); context.Result = new ViewResult { ViewName = "CustomError" }; }
MVCで例外が発生したらメールを送るだけです。メールの送信方法は以下を参考にしてください。
tekitoumemo.hatenablog.com
これでコントローラーに以下のフィルターを追加するとこちらが動きます。
[CustomExceptionFilter]
OnExceptionは例外を拾ってくれるのですが404とか拾ってくれないので以下のコードをStartup.csにDIします。
if (env.IsDevelopment ()) { app.UseDeveloperExceptionPage (); } else { app.UseExceptionHandler ("/Home/Error"); } app.UseStatusCodePagesWithReExecute("/Home/Error");
app.UseExceptionHandler
例外が発生した際に引数で指定したURLのハンドラを実行します。こちらも2.1ならプロジェクト作成したときに自動生成されます
app.UseStatusCodePagesWithReExecute
元のステータスコードをクライアントに返します。引数で指定したURLのハンドラも実行します。こちらは自動生成されないのでDIしなければいけません。
これで400番台のエラーの対応も出来ました。次にエラー用のアクションメソッドを用意します。
エラー用のアクションメソッド
こちらはデフォルトで出来ている( 2.1なら)はずなので僕の実装したコードを記載します。
public IActionResult Error () { switch (HttpContext.Response.StatusCode) { case StatusCodes.Status404NotFound: // 404の処理 break; case StatusCodes.Status500InternalServerError: // 500の処理 break; } return View (); }
HttpContext.Response.StatusCodeからHTTPステータスコードを取得できるのでステータスコードに応じたビューモデルの生成などの処理を入れていけば良いと思います。
処理の順番
Controllerで例外が発生したらどの順番で動くか検証しました。
Controller(例外!!)→例外フィルター→UseExceptionHandlerで指定したURL
例外フィルタの動作は以下のようです。Controllerで補足したエラーをフィルターが拾ってそのあとにUseExceptionHandlerが動く感じですかね。
これらの実装が正しいのかわかりませんが、公式を見て自分なりに実装した感じではこれが妥当かなと思いました。
次のブログでは起動時の例外処理、つまりMVC以外でエラーが起きたときのエラーハンドリングを紹介しようかと思います。こちらは公式の説明がシンプルなので割と参考になると思います。公式の説明は以下です。
captureStartupErrors と detailedErrors キーを利用し、起動中のエラーに対するホストの動作を構成できます。
雑すぎぃ!!
参考
ASP.NET Core のエラーを処理する | Microsoft Docs
ASP.NET Core フィルター | Microsoft Docs
【.Net Core】.Net Coreでも正式にTransactionScopeが使えるようになった!
トランザクションを書くとき、成功したらcommit、失敗したらrollbackと結構めんどいのですが、それらを解決してくれるのがTransactionScopeです。例で書いた方がわかりやすいので以下で説明します。例はDapperを使います。
通常のトランザクション
cn.Open (); using (var tr = cn.BeginTransaction ()) { try { cn.Execute ("UPDATE TEST SET Hoge = @Hoge Where ID = @ID", new { Hoge = 1, ID = 1 }, tr); // コミット tr.Commit (); } catch (Exception e) { // ロールバック tr.Rollback (); } }
TransactionScopeのトランザクション
using (var tr = new TransactionScope ()) { cn.Open (); cn.Execute ("UPDATE TEST SET Hoge = @Hoge Where ID = @ID", new { Hoge = 1, ID = 1 }); // コミット 落ちたらここにたどり着かないのでコミットされずにrollback?される(元に戻る?) tr.Complete (); }
通常に比べてエラー処理など必要なく非常に楽です。Dapperの拡張ライブラリなどクエリを発行ごとにコネクションを作ってたりするので通常で使うには拡張しなければいけない場合があります(拡張ライブラリを拡張するとか、もうね)
System.Data.SqlClientの4.5 previewでは使えたのですが、正式のNugetには上がっていませんでした。
が
10日前にNugetに上がっていました!4.5.0-rc1ってやつです。
www.nuget.org
ちなみに「-rc」はNugetパッケージのプレリリースを指していて完全な正式版ではないのでお間違えないように(Nugetに上がってればおっけいでしょ)
System.Data.SqlClientのほかにSystem.Diagnostics.DiagnosticSourceも入れる必要があります。
www.nuget.org
導入方法
System.Data.SqlClientを追加
dotnet add package System.Data.SqlClient --version 4.5.0-rc1
System.Diagnostics.DiagnosticSourceを追加
dotnet add package System.Diagnostics.DiagnosticSource --version 4.5.0-rc1
※ 注意!「System.Data.SqlClient」と「System.Diagnostics.DiagnosticSource」はバージョンを合わせてください!落ちます。
ソース
using System.Data.SqlClient; using System.Transactions; using (var tr = new TransactionScope ()) { cn.Open (); cn.Execute ("UPDATE TEST SET Hoge = @Hoge Where ID = @ID", new { Hoge = 1, ID = 1 }); tr.Complete (); }
正式に?ってのはちょっと語弊があったかもしれませんがNugetに上がったので利用しやすくなっていると思います!Nugetじゃなくても良いって人は4.6preiewが出ているので試しても良いかもしれません。公式は以下に貼っておきます。
dotnet-core - System.Data.SqlClient 4.6.0-preview3-26501-04 - MyGet - Hosting your NuGet, npm, Bower, Maven, PHP Composer and Vsix packages
AutoValidateAntiforgeryTokenが便利すぎて感動した
今の現場がレベル高くて全く付いていけなくてひたすら給料泥棒してます、よくクビにならんわな。
みんなの洋楽ランキングを絶賛移行中で色々ネタが増えました。それが.Net Coreから追加されたCRLF対策のAutoValidateAntiforgeryTokenで最強だと知りました。あんまりよくわからないけどセキュリティ対策はシンプルに明示的にやる必要ないって考えから出来たみたい。
github.com
今までのValidateAntiForgeryTokenはこんな感じで実装してました。
Controller側
using Microsoft.AspNetCore.Mvc; [ValidateAntiForgeryToken] public ActionResult Index ( { .... }
cshtml側
... @Html.AntiForgeryToken() ....
これでクライアント側でトークンを発行してトークンが違ったら400(Bad Request)になるという仕組み。別にこれでも特に問題がなかったのですが、もっと便利な仕組みが.Net Coreで追加されました。
それがAutoValidateAntiforgeryTokenです。
AutoValidateAntiforgeryToken属性が追加Postアクションはクライアント側(html)で明示的に指定しなくてもトークン認証をしてくれるものです。どういうことかというと以下の通り。
Controller側
using Microsoft.AspNetCore.Mvc; [AutoValidateAntiforgeryToken] public ActionResult Index ( { .... }
cshtml側
...
// なにもいらないよ!!
....
これで以下のhtmlが生成されます。
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8HJB_Oodagkasdfghjgk">
Attributeなのでクラスにつけられるので素敵すぎる!私はベースのコントローラー作ってそれを継承して使ってるので全てのコントローラーに適応されます。以下みたいな感じ。
BaseController
[AutoValidateAntiforgeryToken] public class BaseController : Controller { ......... }
HomeController
public class HomeController : BaseController { ......... }
セッション操作などBaseControllerを用意すると便利?(設計のことはわからないけれども)なのでAutoValidateAntiforgeryTokenも付けられるとめっちゃ使い勝手がよくて最高でした。ajaxとか使ってても以下のような感じでいけると思うのでそこまで困らないかなと思います(試してないけど
var token = $('input[name="__RequestVerificationToken"]').val(); $.ajax({ url: '/home/index/', type: 'POST', data: {__RequestVerificationToken: token}, dataType: 'JSON', success: function (data) { console.log(data); }