tekitoumemo’s diary

思ったことを書くだけ。長文版Twitter

【ASP.NET MVC Core】.NET CoreでSystem.Net.Mail.SmtpClientが使えなくなったのでMimeKitを使う

f:id:tekitoumemo:20180510224948p:plain

みんなの洋楽ランキングを.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内のメソッドが検知出来ない

https://cdn-ak.f.st-hatena.com/images/fotolife/o/ochimusha01/20170827/20170827055211.jpg

最近、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デバッグしたら以下のエラー。。
f:id:tekitoumemo:20180428161901p:plain
意味不明なエラーが出た、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の環境変数を設定してるのに!
f:id:tekitoumemo:20180428163557p:plain

いい記事を発見。

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

macGUIアプリケーションは.bash_profileに設定した環境変数を継承しません。なのでアプリ側にnodeのPATHを設定しないとnodeがないと言われちゃうみたいです。これはmacの仕様なので諦めてね♡ってことみたい。

VSCodeではlaunch.jsonに記載すればおっけいです。vscode環境変数は「env」なのでそこに設定してあげましょう。

"env": {
    ...
    "PATH": "/Users/{自分の名前}/.nodebrew/current/bin" // 参考なので設置したパス
},

で実行。

f:id:tekitoumemo:20180428163044p:plain

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レコードを設定する

f:id:tekitoumemo:20180417212055p:plain
ココらへんは事業所にしたがってやって下さい。Azureは以前に記事を書いたので参考になれば。
tekitoumemo.hatenablog.com

【超重要】リダイレクト設定

超需要です。リダイレクト設定は必ず行いましょう。Linuxなら.htaccessWindowsなら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の評価を下げたくなければ絶対にやる必要があります。やり方は設定ボタンの「アドレス変更」をクリックします。
f:id:tekitoumemo:20180417214002p:plain
そしたらそれぞれの項目をチェックします。

  • リストから新しいサイトを選択する

取得したドメインを選択して下さい。取得したドメインはSearch Consoleに追加して下さい。

  • 301 リダイレクトが正常に動作していることを確認する

「【超重要】リダイレクト設定」を参考にしてください。

  • 確認方法がまだ残っていることを確認する

変更前と変更後のドメインが確認できれば良いと思います。

  • アドレス変更のリクエストを送信する

上記がすべてOKの場合、送信を押します。
f:id:tekitoumemo:20180417214054p:plain

【やったほうが良い】サイトマップを作成する

「みんなの洋楽ランキング」では、サイトマップを自動生成しています。いつでも自動で作成出来ることによって、ページを更新する度にクローラーが来てくれるので、ここは早めにやっちゃいましょう!Fetch as Googleは1ページのみのインデックスリクエストで、サイトマップはサイト全体のリクエストになります。そのため、コンテンツサイトなど全体のページを見てくれるのでてっとり早いです。
f:id:tekitoumemo:20180417214821p:plain

【やったほうが良い】周囲に告知

仕事なら職場の人や利用してくれる人への告知。個人ならTwitterなどの告知など手段はあるので、状況に合わせて実施します。私はTwitterで告知しました。

これでGoogleインデックスの引き継ぎやその他もろもろが完了です。

安全に出来てよかった。

ubuntuでログインループ

詰んだ。対処したのでメモ。

こんな症状が起きた。
youtu.be
なんで?Nvidiaのドライバを入れたら起きるとのことですが、Nvidiaのドライバなんぞ入れてません。

僕の場合はデスクトップ環境が何らかの原因で壊れたことによってログイン出来なかったみたいです。
対処法にlightdmが書いてあったので指示にしたがって対応。
デスクトップからコンソールモードに変更するためctrl + alt + f1を入力。

パッケージ取得

$ sudo dpkg-reconfigure lightdm

削除&インストール

$ sudo apt-get purge lightdm && sudo apt-get install lightdm

再起動して、ログイン。

完了。

参考
www.computersnyou.com

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 mvcdotnet 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

f:id:tekitoumemo:20180415223505p:plain