.NET core 3 版:IServiceCollectionの拡張(ConfigureServicesのアレ)(WebApi ビルドの場合)

.NET Core2.2時代に、WebApi(REST)開発を行う際、.AddMvc()が差し込まれるのが辛かったので、必要なものだけを差すように書き直していたのですが、.NET Core 3(RC)版のIServiceCollectionの拡張を見たら、少し変わっていたので、中を確認してみました。

.NET Core2.2時代のMicrosoft.AspNetCore.Mvc [ MvcServiceCollectionExtensions ]

f:id:CreatioVitae:20190919130629p:plain
Core2.2

        public static IMvcBuilder AddMvc(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            var builder = services.AddMvcCore();

            builder.AddApiExplorer();
            builder.AddAuthorization();

            AddDefaultFrameworkParts(builder.PartManager);

            // Order added affects options setup order

            // Default framework order
            builder.AddFormatterMappings();
            builder.AddViews();
            builder.AddRazorViewEngine();
            builder.AddRazorPages();
            builder.AddCacheTagHelper();

            // +1 order
            builder.AddDataAnnotations(); // +1 order

            // +10 order
            builder.AddJsonFormatters();

            builder.AddCors();

            return new MvcBuilder(builder.Services, builder.PartManager);
        }

VisualStudio2019でスキャフォールディングされるコードでは、.AddMvc()を呼び出しますが、WebApiを構築する場合、色々要らないものがあるので、自分でメソッドを切る人が大半だったと思います。 自分が書く場合は、Viewに関するものは省いて、StaticFileも省いて、Cors設定書いて、JsonOutputFormatterをUTF8Jsonに差し直して……ってやることが多いです。

        public static IMvcCoreBuilder CreateDefaultBuilder(this IServiceCollection serviceDescriptors) {
            return serviceDescriptors
                .AddMvcCore()
                .AddApiExplorer()
                .AddAuthorization()
                .AddCors(option => { {Cors.PolicyName}, builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());})
                .AddDataAnnotations()
                .AddFormatterMappings()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                .AddMvcOptions(
                    option => {
                        option.OutputFormatters.Clear();
                        option.OutputFormatters.Add(new Utf8Json.AspNetCoreMvcFormatter.JsonOutputFormatter(StandardResolver.ExcludeNullCamelCase));
                        option.InputFormatters.Clear();
                        option.InputFormatters.Add(new Utf8Json.AspNetCoreMvcFormatter.JsonInputFormatter(StandardResolver.ExcludeNullCamelCase));

                    }
                );
        }

.NET Core3時代のMicrosoft.AspNetCore.Mvc [ MvcServiceCollectionExtensions ]

f:id:CreatioVitae:20190919152713p:plain
.NET Core3

.AddMvc()の他に、.AddControllers()、.AddControllersWithViews()、.AddRazorPages()が追加されています。 .AddControllers()の中ではAddControllersCore()というPrivateメソッドを呼んでいます。(このAddControllersCore()は.AddControllersWithViews()でも呼ばれてます。)

        public static IMvcBuilder AddControllers(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            var builder = AddControllersCore(services);
            return new MvcBuilder(builder.Services, builder.PartManager);
        }

        private static IMvcCoreBuilder AddControllersCore(IServiceCollection services)
        {
            // This method excludes all of the view-related services by default.
            return services
                .AddMvcCore()
                .AddApiExplorer()
                .AddAuthorization()
                .AddCors()
                .AddDataAnnotations()
                .AddFormatterMappings();
        }

WebApiをビルドする場合、.AddControllers()を呼ぶだけで、使い始められるくらい、必要なものだけ差し込まれてる印象です。 CorsやJsonOutputFormatterのセットアップは変わらず行う必要があるので、実際は結局色々書く気はするんですが、随分すっきりした印象ですね。素敵。

RedisをASP.NET SessionState,OutputCacheのストレージに設定する。

RedisをASP.NET SessionState,OutputCacheのストレージに設定する。

Nugetパッケージのインストール

Microsoft.Web.RedisSessionStateProvider、Microsoft.Web.RedisOutputCacheProvider を追加します。
パッケージを追加するとWeb.ConfigにSessionStateセクション、cachingセクションのコードが挿入されます。
既存のSessionStateセクション、cachingセクションの記述が消えるわけではないので注意してください。

Web.Configの書き換えを行う。

既存のSessionStateセクション、cachingセクションの記述を以下に切り替えます。
尚、シングルインスタンスかRedis-Clusterかによって書き方が若干異なります。

Snipet(SessionState Redis Single Instance)
    <sessionState mode="Custom" customProvider="CustomRedisSessionStateStore" cookieName="{任意のSessionStateのCookie名}" cookieless="false">
      <providers>
        <add name="CustomRedisSessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="{割り当てたいRedisホスト名 or IP}" accessKey="" port="{割り当てたいRedisポート番号}" ssl="false" applicationName = "{任意のApplication名}" databaseId = "0" connectionTimeoutInMilliseconds = "10000" />
      </providers>
    </sessionState>
Snipet(Output Cache Redis Single Instance)
    <caching>
      <outputCache defaultProvider="CustomRedisOutputCache">
        <providers>
          <add name="CustomRedisOutputCache" type="Microsoft.Web.Redis.RedisOutputCacheProvider" host="{割り当てたいRedisホスト名 or IP}" accessKey="" port="{割り当てたいRedisポート番号}" ssl="false" applicationName="{任意のApplication名}"  databaseId="1"  connectionTimeoutInMilliseconds="10000" />
        </providers>
      </outputCache>
    </caching>
Snipet(SessionState Redis Cluster)
    <sessionState mode="Custom" customProvider="CustomRedisSessionStateStore" cookieName="{任意のSessionStateのCookie名}" cookieless="false">
      <providers>
        <add name="CustomRedisSessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider" connectionString="{PrimaryNodeのホスト名 or IP}:{PrimaryNodeのポート番号},{SecondaryNodeのホスト名 or IP}:{SecondaryNodeのポート番号}" accessKey="" ssl="false" applicationName="{任意のApplication名}" connectionTimeoutInMilliseconds="10000" />
      </providers>
    </sessionState>
Snipet(OutputCache Redis Cluster)
    <caching>
      <outputCache defaultProvider="CustomRedisOutputCache">
        <providers>
        <add name="CustomRedisOutputCache" type="Microsoft.Web.Redis.RedisOutputCacheProvider" connectionString="{PrimaryNodeのホスト名 or IP}:{PrimaryNodeのポート番号},{SecondaryNodeのホスト名 or IP}:{SecondaryNodeのポート番号}" accessKey="" ssl="false" applicationName="{設定したいApplication名}" connectionTimeoutInMilliseconds="10000" />
        </providers>
      </outputCache>
    </caching>

Visual Studio 2017へ.NET Framework 4.7.2の追加を行う

前提条件

Visual Studio Professional 2017(VS 15.7.2)
Windows 7 Pro

の環境で、試行しています。

やりたいこと

Visual Studio 2017で.NET Framework 4.7.2を利用して開発が出来るようにしたい

記事の目的

2018/5/26現在、ちょっとしたトラップがあったので、メモがてら残しておきます。(探すの地味に大変なので。)
そのうち改善されそうだけど、今のところやや迷惑なので。

RuntimeとDeveloper Packをインストールする

Visual Studioのターゲットフレームワークの選択プルダウンで[別のフレームワークをインストールします]を選択すると以下に飛ばされます。
www.microsoft.com

ここから4.7.2のRuntimeと、Developer Packを取得します。

Runtimeは取得できるけど、Developer Packの英語版(要するに本体)が取得できない。

4.7.2のDeveloper Packのリンク踏むとページ遷移先で.NET Framework 4.7.2の日本語言語パック(NDP472-DevPack-JPN.exe)がダウンロードされます。

言語パックも必要ですが、先ずはNDP472-DevPack-ENU.exeを手に入れないといけないので、
.NET Framework の新機能 | Microsoft Docs
ページの[.NET Framework 4.7.2 Developer Pack]リンクから取得します。

そしてインストールへ

ランタイム→Developer Pack@ENU →Developer Pack@JPN
の順番にインストールすれば、Visual Studioでターゲットフレームワークの一覧に4.7.2が出現します。

Memo:インターネット加盟店のクレジット決済@カード情報非保持サービスについて

インターネット加盟店のクレジット決済@カード情報非保持サービスについて、調べる機会があったので、自分へのメモも兼ねてまとめておきます。

インターネット加盟店のクレジット決済@カード情報非保持サービスについて

決済代行のカード情報非保持サービスは二種類ある

→リンク(画面遷移)型とデータ伝送(モジュール、Api)型。

リンク型って?

加盟店側で決済方法としてカード決済を選択すると、カード会員データの入力画面(@決済代行事業者のサーバー)に切り替わる。

メリット:

→カード会員データは加盟店側のサーバで伝送も処理も保存もされない。(加盟店側のシステムに対して、カード番号とかが通過しない。保持もしないし、トンネルもしない。)

デメリット:

外部サイトへの画面遷移が発生することで、購入フロー途中での離脱が多発する。(「フィッシングサイトに飛ばされちゃったかも問題」)

データ伝送型って?

加盟店側で決済方法としてカード決済を選択すると、サイト内のカード会員データ入力画面に切り替わる。

メリット:

→「フィッシングサイトに飛ばされちゃったかも問題」が発生しない。

デメリット:

→システム内をカード番号とかが通過する。「自分たちのサーバーにバックドアが仕掛けられてて、カード番号のオーソリと同時に、カード番号の吸出しが行われちゃったりするかも問題」

結局どっち使うことが多いの?

セキュリティーは大事だからリンク型にしたいところだけど、売上の機会損失は困るから、データ転送型でやるしかないかないかもなー。「通称、セキュリティーはプロフィットを生み出さない問題」

Null合体演算子とNull条件演算子と戯れてみる。

C#6.0で追加されたNull条件演算子、便利ですよね。
今回はNull合体演算子と共に戯れてみます。

例)hogeが、Nullである場合は、string.Empty、そうでない場合はTrim処理を行う。
@Null合体演算子を使う場合

// string.EmptyにもTrim処理がかかって、うざい。あと括弧がうざい。
var fuga = (hoge??string.Empty).Trim();

@Null条件演算子も使う場合

// スッキリ!(あくまで個人の感想です)
var fuga = hoge?.Trim() ?? string.Empty;

三項演算子も含めてですが、コードが煩雑になってしまうと意味がないので、コードがシンプルになるよう気を付けて使いたいです。

ASP.NET WebApiでSwaggerを使ってみる。

導入方法

NugetでSwashbuckleを追加します。

f:id:CreatioVitae:20151224163631p:plain

Nugetでの導入後、SwaggerConfigが作られているため、XMLドキュメントコメントを参照するように変更を加えます。

下記コードがコメントアウトされているので、コメントアウトを解除します。

c.IncludeXmlComments(GetXmlCommentsPath());

GetXmlCommentsPathメソッドがないと怒られるので、GetXmlCommentsPathメソッドを作成します。

private static string GetXmlCommentsPath()
{
    return string.Format(@"{0}\bin\[プロジェクト名].XML", System.AppDomain.CurrentDomain.BaseDirectory);
}

XMLの参照先については、ビルド時に出力したXMLを指定すればOKです。

※各アクションのヘッダーコメントは、XMLドキュメントコメントで記述してください。
詳細は下記参照してください。(といっても、いつものスラッシュ3つで付けるアレです。)
https://msdn.microsoft.com/ja-jp/library/b2s063f7.aspx

アプリを起動したら、[ドメインURL(localhostだったらlocalhost:ポート番号)/swagger/ui/index]をGetでたたくことで、Swagger Uiが表示されます。

f:id:CreatioVitae:20151224170448p:plain

f:id:CreatioVitae:20151224170501p:plain

ルートパラメーター、クエリーパラメーター、リクエストボディ等の仕様が表示されます。

記述したアクションのヘッダーコメントは、アクション名の右側に表示されます。

URLReWriteを使って、リダイレクトを設定する。

Webサイト/アプリを運営していると、ページの評価の統一をしたい等の理由で、リダイレクトを設定したいケースはままあるかと思います。
その際、C#等のプロダクトコードでリダイレクトかけるのは少々面倒なので、IISのURLReWriteにて、設定をしてしまいます。

@準備

URLReWriteは下記URLからダウンロードして適用するか、Web Platform Installer経由でインストールしましょう。
Download Microsoft URL Rewrite Module 2.0 for IIS 7 (x64) from Official Microsoft Download Center

@規則設定

今回は以下について、設定を行います。

  • トレイリングスラッシュ無しパターン
  • 大文字混入パターン
  • トレイリングスラッシュ無し&大文字混入パターン
Case1:トレイリングスラッシュ無しパターン

これは、URLReWriteの規則のテンプレートに用意されているので、基本いじることはありません。
(設定の詳細は画像参照。正規表現パターンとリダイレクト先は、スニペット参照)

項目 スニペット
パターン (.*[^/])$
リダイレクトURL https://{SERVER_NAME}/{URL}/

f:id:CreatioVitae:20151201142811p:plain
f:id:CreatioVitae:20151201142801p:plain

Case2:大文字混入パターン

これもURLReWriteの規則のテンプレートに用意されているのですが、このままだと、JavaScriptや、CSSファイル等も、小文字のURLに書き換えられてしまいます。
条件に、ファイルでない、ディレクトリでないの条件を追加することで、外部ファイルは適用対象外に設定できるので併せて設定してください。
(設定の詳細は画像参照。正規表現パターンとリダイレクト先は、スニペット参照)

項目 スニペット
パターン [A-Z]
リダイレクトURL https://{SERVER_NAME}{ToLower:{URL}}

f:id:CreatioVitae:20151201142902p:plain
f:id:CreatioVitae:20151201142910p:plain

Case3:トレイリングスラッシュ無し&大文字混入パターン

上記二つのパターンの混合パターンです。
上記二つだけでも、リダイレクト自体は可能ですが、2回リダイレクトが発生してしまうため、リダイレクト回数を抑えたい場合は、別途規則を設定します。
(設定の詳細は画像参照。正規表現パターンとリダイレクト先は、スニペット参照)

項目 スニペット
パターン (?=(.*[^/])$)(?=.*[A-Z])
リダイレクトURL https://{SERVER_NAME}{ToLower:{URL}}/

f:id:CreatioVitae:20151201143045p:plain
f:id:CreatioVitae:20151201143054p:plain

実際に設定する場合、Web.Configで設定することもあるかと思いますが、基本的には、URLReWriteに規則を設定した上で、configのスニペットを生成するという流れかと思います。