.NET Core + Vue.jsのプロジェクト作成

自分用のメモ代わりにやったことを書いておきます。
ほぼ参考にさせていただいたページのままですが、そちらのほうではMVCプロジェクトで作成していますが、ここではWebAPIプロジェクトで作成するので若干の違いがあります。

# 環境
- Visual Studio 2019
- .NET Core 3.1.1
- vue cli 4.2.2

# プロジェクト作成

## ASP.NET Core WebAPIプロジェクトを作成
とりあえず認証等は何もつけずにAPIのみ

## Vue.jsプロジェクト作成
作成したプロジェクトのフォルダで以下のコマンドを実行

```
vue create client-app
```

`default (babel, eslint)`を選択

## .NET Coreプロジェクトファイルの編集
csprojファイルを以下のように編集

```xml
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="client-app\**" />
    <Content Remove="client-app\**" />
    <None Include="client-app\**" Exclude="client-app\node_modules\**" />
  </ItemGroup>

  <Target Name="ExecNpmInstall" BeforeTargets="Build" Condition="'$(Configuration)' == 'Debug' And !Exists('client-app\node_modules')">
    <Exec WorkingDirectory="client-app\" Command="npm install" />
  </Target>

  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <Exec WorkingDirectory="client-app" Command="npm install" />
    <Exec WorkingDirectory="client-app" Command="npm run build" />

    <ItemGroup>
      <DistFiles Include="client-app\dist\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>

</Project>
```

## .NET Core側のSPA用Configとデバッグ用コードの追加
SPA用のパッケージが入っていないのでnugetで以下のパッケージをインストール

- Microsoft.AspNetCore.SpaServices.Extensions

ConfigureServicesにSPA用の記述を追加

```C#:Startup.cs
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            // 追加
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = @"client-app/dist";
            });
        }
```

ConfigureにSPA用の記述を追加

```C#:Startup.cs
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            // 追加
            app.UseSpaStaticFiles();
            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "client-app";
                if (env.IsDevelopment())
                {
                    spa.UseProxyToSpaDevelopmentServer(async () =>
                    {
                        var pi = new ProcessStartInfo
                        {
                            FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd" : "npm",
                            Arguments = $"{(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "/c npm " : "")}run serve",
                            WorkingDirectory = "client-app",
                            RedirectStandardError = true,
                            RedirectStandardInput = true,
                            RedirectStandardOutput = true,
                            UseShellExecute = false,
                        };
                        var p = Process.Start(pi);
                        var lf = app.ApplicationServices.GetService<ILoggerFactory>();
                        var logger = lf.CreateLogger("npm");
                        var tcs = new TaskCompletionSource<int>();
                        _ = Task.Run(() =>
                        {
                            var line = "";
                            while ((line = p.StandardOutput.ReadLine()) != null)
                            {
                                if (line.Contains("DONE  Compiled successfully in "))
                                {
                                    tcs.SetResult(0);
                                }

                                logger.LogInformation(line);
                            }
                        });
                        _ = Task.Run(() =>
                        {
                            var line = "";
                            while ((line = p.StandardError.ReadLine()) != null)
                            {
                                logger.LogError(line);
                            }
                        });
                        await Task.WhenAny(Task.Delay(20000), tcs.Task);
                        return new Uri("http://localhost:8080");
                    });
                }
            });
        }
```

一応ここまでで動作はしますが、デフォルトだとデバッグ実行した時にWeatherForecastにアクセスするようになっているためプロジェクトのプロパティで以下の画像の`ブラウザの起動`の右にあるテキストボックスを空にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277868/8fe61e25-e7b4-6296-9d1c-baa6bebd2408.png)


## 実行
デバッグ実行して問題がなければVue.jsプロジェクトのほうのページが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277868/b77de33b-c424-ecd9-2c1d-b0c403d84ff6.png)


# 参考
https://blog.okazuki.jp/entry/2019/06/17/132755