【ASP.NET MVC Core】.NET CoreでSystem.Net.Mail.SmtpClientが使えなくなったのでMimeKitを使う
みんなの洋楽ランキングを.NET Coreに絶賛移行中でMacBook Airのみで開発しています。.Net Core3.0ではWindows Formも対応するみたいでなかなか最強感出てきましたね!ASP.NET CoreはScalaの1.7倍のパフォーマンスを出すそうです。
www.ageofascent.com
タイトル通り「.NET CoreでSystem.Net.Mail.SmtpClientが使えなくなった」です。
.NET CoreからSystem.Net.Mail.SmtpClientでなくMimeKitが推奨されましたのでそのメモ。結構この記事は多いのですが、デバッグ時にSSL推奨の影響でエラーになっちゃったりするんでその対応を追記します。
MimeKitとは?
github.com
オープンソースのメールライブラリです。公式がこう発表しています。
「SmtpClientとそのタイプのネットワークは設計が不十分であり、代わりに MailKitを使うことを強く推奨する」
という感じです。
MimeKitをインストール
バージョンは以下で確認してくださいGithubの最新は2.02だった。なぜ?
www.nuget.org
以下のコマンドで追加
dotnet add package MailKit --version 2.0.3
csprojに
<PackageReference Include="MailKit" Version="2.0.3" />
を追加して
dotnet restore
でも良いです。
使い方
usingと生成
using MimeKit; var emailMessage = new MimeMessage ();
送信元とユーザー名を設定
emailMessage.From.Add (new MailboxAddress ("ユーザー名", "送信元"));
送信先の設定
emailMessage.To.Add (new MailboxAddress ( "送信先"));
件名と本文を設定
emailMessage.Subject = "件名"; emailMessage.Body = new TextPart ("plain") { Text = "本文" };
SMTPサーバに接続
var client = new SmtpClient(); await client.ConnectAsync("smtp.test.net", 587, SecureSocketOptions.SslOnConnect);
SMTPサーバ認証IDとパスワード)
await client.AuthenticateAsync("SMTPサーバ認証ID", "パスワード");
メールを送信
await client.SendAsync(emailMessage);
SMTPサーバ接続を切る
await client.DisconnectAsync(true);
処理はこれで終了です。ですが、開発中にSSLにしていないとエラーになります。
エラーになるコードは以下です。
var emailMessage = new MimeMessage (); //と await client.ConnectAsync("smtp.test.net", 587, SecureSocketOptions.SslOnConnect);
で以下のエラーが発生します。
SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
SSL接続が出来ないってことですね。なので対策のため、生成前に証明書検証用コールバックを設定します。
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; var emailMessage = new MimeMessage ();
SecureSocketOptionsはStartTlsを設定します。StartTlsは受信側がTLS/SSLに対応していれば暗号化して送り、されていなければ平文で送るオプションです。
await client.ConnectAsync("smtp.test.net", 587, SecureSocketOptions.StartTls);
ちなみにGmailは以下のコードにメールアドレスとパスワードを設定すればよいです。
await client.AuthenticateAsync("SMTPサーバ認証ID", "パスワード");
ざっと通しで作ったので確認してみてください。
using System.Linq; using System.Net; using System.Threading.Tasks; using MailKit.Net.Smtp; using MailKit.Security; using MimeKit; namespace Mail { public class MailManager { public string UserName { get; set; } public string PassWord { get; set; } public string From { get; set; } public string To { get; set; } public string Subject { get; set; } public string Body { get; set; } public string Host { get; set; } public int Port { get; set; } public async Task SendEmailAsync () { #if DEBUG ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; #endif var emailMessage = new MimeMessage (); emailMessage.From.Add (new MailboxAddress (this.UserName, this.From)); emailMessage.To.Add (new MailboxAddress (this.To)); emailMessage.Subject = this.Subject; emailMessage.Body = new TextPart ("plain") { Text = this.Body }; using (var client = new SmtpClient ()) { #if DEBUG await client.ConnectAsync (this.Host, this.Port, SecureSocketOptions.StartTls); #else await client.ConnectAsync (this.Host, this.Port, SecureSocketOptions.SslOnConnect); #endif await client.AuthenticateAsync (this.UserName, this.PassWord); await client.SendAsync (emailMessage); await client.DisconnectAsync (true); } } } }
最低限必要なものはプロパティで用意しているので、設定すれば動くと思います。
async、awaitが標準になり使い勝手も変わらないので結構楽でよいですね!
【Jasmine、TypeScript】JasmineのSpyOnでsubscribe内のメソッドが検知出来ない
最近、SPAが流行っていて私もAngular5に四苦八苦しています。Angularの(っていうかJSの)テストフレームワークの「Jasmine」でテストしながら開発を進めているのですが、早速詰んだので解決方法を書きます。
「JasmineのSpyOnでsubscribe内のメソッドが検知出来ない」とはどういう状況?
以下のコードからngOnInitでroute.eventsのsubscribeで呼び出しているscrollを検知したいので、テスト側でSpyOnして実行します。が、これが失敗します。alertで呼び出されていることがわかるので実際には動いているはずです。
コード側
public ngOnInit() { this.route.events.subscribe(event => { if (event instanceof NavigationEnd) { window.scroll(100, 100); alert("ここ呼ばれているはずだよ!!"); } }) }
テスト側
import { assert } from 'chai'; import { CounterComponent } from './counter.component'; import { TestBed, async, ComponentFixture } from '@angular/core/testing'; import { RouterModule , Router, NavigationEnd ,Routes } from "@angular/router"; import {APP_BASE_HREF} from '@angular/common'; let fixture: ComponentFixture<CounterComponent>; const appRoutes: Routes = [ {path: 'counter', component: CounterComponent} ]; describe('Counter component', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [CounterComponent], imports: [ RouterModule.forRoot(appRoutes) ], providers: [{provide: APP_BASE_HREF, useValue : '/' }] }) fixture = TestBed.createComponent(CounterComponent); fixture.detectChanges(); }); it('test', async(() => { let router: Router; router = TestBed.get(Router); spyOn(window, "scroll"); router.navigate(["/counter"]); fixture.componentInstance.ngOnInit(); expect(window.scroll).toHaveBeenCalled(); })); });
実行すると呼び出されていないよというエラーが出ます。
Expected spy scroll to have been called
これは単純にexpectが呼び出されるときにsubscribeが動いていないってことでした。subscribeは実行後の後続処理的なやつで非同期で動きます。なので、expectしたときにまだ動いてなくて検知出来なかっただけというオチでした。
以下が変更点です。
it('test', fakeAsync(() => { let router: Router; router = TestBed.get(Router); spyOn(window, "scroll"); router.navigate(["/counter"]); fixture.componentInstance.ngOnInit(); tick(); // or flushMicrotasks() expect(window.scroll).toHaveBeenCalled(); }));
fakeAsyncで定義してtick()で処理を待ちます。flushMicrotasksでも良いですが、tickで待ってから評価した方が良い気がします。
むずいわ。
macで.NET CoreのSPAプロジェクトを動かすと謎のエラー
macでもコマンドベースだとSPAがちゃんと動きます。以下の通り。
dotnet new angular -o angularTest npm i webpack dotnet restore dotnet build dotnet run
これで動くのでvscodeでデバッグしたら以下のエラー。。
意味不明なエラーが出た、ubuntuでは動いたのに。。
発生したコード
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { HotModuleReplacement = true });
例外メッセージ
例外が発生しました: CLR/System.AggregateException An exception of type 'System.AggregateException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'One or more errors occurred.' Inner exceptions found, see $exception in variables window for more details. Innermost exception System.ComponentModel.Win32Exception : No such file or directory at System.Diagnostics.Process.ResolvePath(String filename) at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo) at System.Diagnostics.Process.Start() at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) at Microsoft.AspNetCore.NodeServices.HostingModels.OutOfProcessNodeInstance.LaunchNodeProcess(ProcessStartInfo startInfo)
これは、angular(フロントJS)をIISで動かそうとしてるのでファイルがねーぞって言われているエラーです。
nodeの環境変数を設定してるのに!
いい記事を発見。
GUI applications on the Mac do not use inherit any environment variables that are defined for the terminal neither do they run any bash profile scripts. So option 2 is probably the best option.
VS Mac adds the following two paths to its PATH environment variable.
/usr/local/bin
/Library/Frameworks/Mono.framework/Commands
I do not believe there are any plans to have VS Mac somehow execute the bash profile script.
VS2017 for Mac - UseWebpackDevMiddleware crash on application start - Developer Community
macのGUIアプリケーションは.bash_profileに設定した環境変数を継承しません。なのでアプリ側にnodeのPATHを設定しないとnodeがないと言われちゃうみたいです。これはmacの仕様なので諦めてね♡ってことみたい。
VSCodeではlaunch.jsonに記載すればおっけいです。vscodeの環境変数は「env」なのでそこに設定してあげましょう。
"env": { ... "PATH": "/Users/{自分の名前}/.nodebrew/current/bin" // 参考なので設置したパス },
で実行。
macでも環境できてよかったわー。
【C#】テキトーなAutoMapperを作った
AutoMapperがあまり使い勝手良くなかったから自分で作りました。クラスの型が違っててもだいたいマップするよってライブラリ。
メソッド
これだけ(笑)
void Map<T1, T2>(T1 src, T2 dest)
- T1:コピー元のクラス型
- T2:コピー先のクラス型
- src:コピー元のモデル
- dest:コピー先のモデル
結果はdestにsrcの内容がコピられるってだけです。
使い方
var suitableMapper = new SuitableMapper();
suitableMapper.Map<Test1, Test2>(model1, model2);
どうなるかは以下で説明します。
// Test1クラスのプロパティと値 hoge1 = "TEST1"; // string型 hoge2 = 1; // int型 hoge3 = true; // bool型 hoge4 = new Hoge(){ hoge1 = "OK!"}; / / Hogeクラス型 // Test2クラスのプロパティと値 hoge1 = "TEST2"; // string型 hoge2 = 5; // int型 hoge3 = 10; // int型 hoge4 = null / / Hogeクラス型 // 実行 suitableMapper.Map<Test1, Test2>(model1, model2); // Test2クラスにコピーされます。 hoge1 = "TEST1" hoge2 = 1 hoge3 = 10 hoge4 = new Hoge(){ hoge1 = "OK!"}
Test2クラスのhoge3だけは型が違うのでコピーされず、他の3つはTest1クラスのデータがコピーされます。
なぜ作ったか?
AutoMapperは完全に同じクラスじゃないとマッピングしないので、結局手動でマップしたりAutoMapperに型を合わせたりなどいろいろめんどくさいことがあります。画面とデータは対じゃないのでそこの間を吸収出来ないかと思ったことがきっかけです。とりあえず名前と型が一致してたらコピーしちゃえという適当AutoMapperと思ってくれれば良いです。
使えないところ
名前と型が完全に一致してなければダメです。例えばIDとHatenaIDは同じでマップしたくても名前が違うのでダメです。あとクラスは参照型なのでコピーされたらコピー前のデータが残りません。あっあと.NetCoreで作ったので.NetFrameworkでは使えませんが、ソースをコピれば使えます。
注意
テストとかしてないので、使えそうなところを参考程度に見てもらえるとありがたいです。いつかgithubのReadmeもちゃんと書きます。
ドメイン変更でやったことまとめ
先月公開した「みんなの洋楽ランキング」のドメイン変更を行いました。公開して2週間なので、大した影響はないのですが一応ちゃんとやりましたので記事に残しておきます。
ちなみに、以下にように変更しました。
https://mygkrnk.azurewebsites.net ⇛ https://mygkrnk.com/
Azureドメインから変更する感じですね。よく「WEBサービスを作った」的な記事を見てるんですが、herokuドメインのままだったりとドメイン変更やSSL導入に抵抗があるように感じます。ドメイン変更は早い段階でやらないと厄介なので早めにやっちゃいましょう!
まずは費用
1000円程度です。お名前.comで取得したドメイン代ぐらいです。
ドメインを取ります
www.onamae.com
僕はお名前ドットコムで取りましたがどこでもよいです、別に事業所によって変わったりしないので。「.com」なら一年で900円ぐらい。ちゃちゃっと購入しましょう。
SSL導入
以前に記事を書いたので、参考になれば。
tekitoumemo.hatenablog.com
今年の3月からLet’s EncryptでワイルドカードSSLが無料で取得出来るようになりました。この団体は素敵なスローガンを掲げてます。
Let's Encrypt を運営している Internet Security Research Group (略称:ISRG) は、アメリカ合衆国カリフォルニア州にある公益法人で、アメリカ合衆国内国歳入法 Section 501(c)(3) による非課税法人として、アメリカ合衆国内国歳入庁 (IRS) による承認を受けています。
インターネットを介した安全な通信を行う際の、経済面・技術面・教育面での障壁を減らすことが、ISRG の使命です。
ISRG は、誰もがより安全なインターネットがに興味を持つことで、公共の利益のためのデジタル基盤を提供する取り組みを一緒に行うことを可能にするという模範を示すことができると信じています。
素晴らしい!!
さらにGooogleのSSL優遇は必須になってきました。
webmaster-ja.googleblog.com
レンタルサーバーやPaasにDNSレコードを設定する
ココらへんは事業所にしたがってやって下さい。Azureは以前に記事を書いたので参考になれば。
tekitoumemo.hatenablog.com
【超重要】リダイレクト設定
超需要です。リダイレクト設定は必ず行いましょう。Linuxなら.htaccess、WindowsならWeb.config、appsettings.json に記載すればよいです。またこれをやらないと後で説明するSearch Consoleのアドレス変更が出来ませんので必須です。Windows Severのリライトの方法は以下(コピペなので参考程度に)
<rewrite> <rules> <rule name="mygkrnk.azurewebsites.net" stopProcessing="true"> <match url="(.*)" /> <conditions> <add input="{HTTP_HOST}" pattern="^mygkrnk\.azurewebsites\.net$" /> </conditions> <action type="Redirect" url="https://mygkrnk.com/{R:1}" redirectType="Permanent" /> </rule> </rules> </rewrite>
【重要】カノニカルタグを設定
同じサイトでドメインが複数あると重複コンテンツとして評価されてSEOが下がってしまいます。なので、どのサイトが本当のドメインか教えたあげる必要があります。記述方法はヘッダータグに本当のURLを入れます。
<head> <link rel="canonical" href="http://example.com/"> </head>
【超重要】Search Consoleでアドレス変更の申請
Googleの評価を下げたくなければ絶対にやる必要があります。やり方は設定ボタンの「アドレス変更」をクリックします。
そしたらそれぞれの項目をチェックします。
- リストから新しいサイトを選択する
取得したドメインを選択して下さい。取得したドメインはSearch Consoleに追加して下さい。
- 301 リダイレクトが正常に動作していることを確認する
「【超重要】リダイレクト設定」を参考にしてください。
- 確認方法がまだ残っていることを確認する
変更前と変更後のドメインが確認できれば良いと思います。
- アドレス変更のリクエストを送信する
上記がすべてOKの場合、送信を押します。
【やったほうが良い】サイトマップを作成する
「みんなの洋楽ランキング」では、サイトマップを自動生成しています。いつでも自動で作成出来ることによって、ページを更新する度にクローラーが来てくれるので、ここは早めにやっちゃいましょう!Fetch as Googleは1ページのみのインデックスリクエストで、サイトマップはサイト全体のリクエストになります。そのため、コンテンツサイトなど全体のページを見てくれるのでてっとり早いです。
【やったほうが良い】周囲に告知
仕事なら職場の人や利用してくれる人への告知。個人ならTwitterなどの告知など手段はあるので、状況に合わせて実施します。私はTwitterで告知しました。
ドメインを変えました!ブックマークをされている方がいましたらこちらに変更をお願いします。よろしくお願いいたします!https://t.co/NQcC6Otlx7
— みんなの洋楽ランキング (@mygkrnk) 2018年4月11日
これでGoogleインデックスの引き継ぎやその他もろもろが完了です。
安全に出来てよかった。
ubuntuでログインループ
詰んだ。対処したのでメモ。
こんな症状が起きた。
youtu.be
なんで?Nvidiaのドライバを入れたら起きるとのことですが、Nvidiaのドライバなんぞ入れてません。
僕の場合はデスクトップ環境が何らかの原因で壊れたことによってログイン出来なかったみたいです。
対処法にlightdmが書いてあったので指示にしたがって対応。
デスクトップからコンソールモードに変更するためctrl + alt + f1を入力。
パッケージ取得
$ sudo dpkg-reconfigure lightdm
削除&インストール
$ sudo apt-get purge lightdm && sudo apt-get install lightdm
再起動して、ログイン。
完了。
ubuntuで.net coreを動かしたらnuget動かんかったのでその対処とAngular追加
タイトルどおりです。なんにも動かんくなったので、対処法を書く。
まず以下のエラーが発生
Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
おなじみのアセンブリエラーですが、.net coreは.net Frameworkと違ってbin配下にnugetライブラリが入りません。以下のコマンド打つとパスが表示されます。
saito@saito-Aspire-one-1-131:~$ dotnet --info .NET コマンド ライン ツール (2.0.0) Product Information: Version: 2.0.0 Commit SHA-1 hash: cdcd1928c9 Runtime Environment: OS Name: ubuntu OS Version: 16.04 OS Platform: Linux RID: ubuntu.16.04-x64 Base Path: /usr/share/dotnet/sdk/2.0.0/ Microsoft .NET Core Shared Framework Host Version : 2.0.0 Build : e8b8861ac7faf042c87a5c2f9f2d04c98b69f28d
パスが「/usr/share/dotnet/sdk/」なのでlsすると.Net Core1.0からいっぱい入ってました。ログ取るの忘れていたので「2.0」となってますが、本当はPreviewがはいってました
これが原因で、Nuget参照するときにどのバージョンでNugetを参照するかわかんなかったみたいです(これ予想なのであてにしないで下さい)
全部いらないんで、過去のバージョンをすべて削除
sudo apt remove dotnet*
全部なくなりました。次に.Net Core2.0をインストールします。以下の記事を参考に
kledgeb.blogspot.jp
これでdotnet new mvc、dotnet restore、dotnet build、dotnet runをやってみて確認で完了。
次にAngularテンプレートを入れます。Windowsとちょっと違いました。
SPAテンプレートを取得
dotnet new --install Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-rc1-final
いろんな記事に「dotnet new --install Microsoft.AspNetCore.SpaTemplates::*」と書いてあるけど、ubuntuだと出来ませんでした。Windowsだと出来た気がするけど。
ログは出力されてReact、Vueなど様々なスキャフォールディングが使えます、超便利。
Restoring packages for /home/saito/.templateengine/dotnetcli/v2.0.0/scratch/restore.csproj... Installing Microsoft.DotNet.Web.Spa.ProjectTemplates 2.0.0-rc1-final. Generating MSBuild file /home/saito/.templateengine/dotnetcli/v2.0.0/scratch/obj/restore.csproj.nuget.g.props. Generating MSBuild file /home/saito/.templateengine/dotnetcli/v2.0.0/scratch/obj/restore.csproj.nuget.g.targets. Restore completed in 3.7 sec for /home/saito/.templateengine/dotnetcli/v2.0.0/scratch/restore.csproj. テンプレート 短い名前 言語 タグ -------------------------------------------------------------------------------------------------------- Console Application console [C#], F#, VB Common/Console Class library classlib [C#], F#, VB Common/Library Unit Test Project mstest [C#], F#, VB Test/MSTest xUnit Test Project xunit [C#], F#, VB Test/xUnit ASP.NET Core Empty web [C#], F# Web/Empty ASP.NET Core Web App (Model-View-Controller) mvc [C#], F# Web/MVC ASP.NET Core Web App razor [C#] Web/MVC/Razor Pages ASP.NET Core with Angular angular [C#] Web/MVC/SPA ASP.NET Core with React.js react [C#] Web/MVC/SPA ASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPA ASP.NET Core Web API webapi [C#], F# Web/WebAPI global.json file globaljson Config Nuget Config nugetconfig Config Web Config webconfig Config Solution File sln Solution Razor Page page Web/ASP.NET MVC ViewImports viewimports Web/ASP.NET MVC ViewStart viewstart Web/ASP.NET Examples: dotnet new mvc --auth Individual dotnet new mstest dotnet new --help
これで以下のコマンド打てばAngularプロジェクトが完成。
mkdir test cd test dotnet new angular npm i dotnet restore dotnet buld dotnet run