.NET6 ASP.NET Coreで１つのプロジェクトで、ASP.NET WebAPI+Vue3 Viteの環境を作ってみる

## はじめに
- 公式では、ReactとAngularしかないのでVite Vue3 Asp.net Coreの環境を作ってみる。
## 作成方法 
1. VisualStudioを立ち上げで、AngularでのASPNETcoreのテンプレートを選択しプロジェクトを作成する。
![Angular.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613576/d46d56a2-1264-6aa3-47e7-64d5e2ed8f44.jpeg)
2. ClientAppフォルダのaspnetcore-https.js以外を削除する。
![ClientApp.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613576/1bf5be06-3064-f593-a581-ae9266231ac6.jpeg)
3. プロジェクトが保存されていない任意のフォルダで`npm create vite@latest vuetemplate -- --template vue`を実行する。
4. 作成したフォルダにある.vccode フォルダ以外をプロジェクトのClientAppフォルダへコピーする。
![Folder.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613576/6907b5af-4272-7829-b162-1451f3d7599b.jpeg)
![ClientApp2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613576/f1eaae64-6e5d-9a0d-82ff-db677b1ee37a.jpeg)
5. .csprojファイルを開く以降csprojファイルxmlを編集する。
6. PropertyGroup要素にある、SpaProxyLaunchCommandがVisualStudioで実行時に実行される、npmのコマンドなので`npm run dev`に変更する。 
   ```xml:*.csproj
   <PropertyGroup>
     <TargetFramework>net6.0</TargetFramework>
     <Nullable>enable</Nullable>
     <IsPackable>false</IsPackable>
     <SpaRoot>ClientApp\</SpaRoot>
     <SpaProxyServerUrl>https://localhost:44421</SpaProxyServerUrl>
     <SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
     <ImplicitUsings>enable</ImplicitUsings>
   </PropertyGroup>
   ```
   ``` diff_xml
    - <SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
    + <SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>
   ```
7. 下記がDebugBuild後に実行されるコマンドで、node_modulesディレクトリがないと実行される`npm install`を`npm ci`に変更。
   ``` xml:*.csproj
	<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
	<!-- Ensure Node.js is installed -->
	<Exec Command="node --version" ContinueOnError="true">
	  <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
	</Exec>
	<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
	<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
	<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
	</Target>
   ```
   ``` diff_xml
   - <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
   + <Exec WorkingDirectory="$(SpaRoot)" Command="npm ci" />
   ```
8. 下記がDotnet publishで実行されるコマンドなので同様に`npm install`を`npm ci` に`npm run build -- --prod`を`npm run build`に変更する。 
   ``` xml:*.csproj
    <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
	<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
	<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
	<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />

	<!-- Include the newly-built files in the publish output -->
	<ItemGroup>
	  <DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
	  <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
		<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
		<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
		<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
	  </ResolvedFileToPublish>
	</ItemGroup>
    </Target>  
   ```
   ``` diff_xml
   - <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
   - <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
   + <Exec WorkingDirectory="$(SpaRoot)" Command="npm ci" />
   + <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
   ```
9. 同様に、distフォルダをコピーしたいので、`<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" /> `を`<DistFiles Include="$(SpaRoot)dist\**/>`に変更
   ``` diff_xml
   -  <DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
   +  <DistFiles Include="$(SpaRoot)dist\** />
   ```
10. ClientAppフォルダでターミナルを開き、npm install を行う
11. csprojファイルの`<SpaProxyServerUrl>`要素で、Asp.netcore からnpmで起動されるポートが確認できる。今回の場合はhttps://localhost:44421です。
12. ASP.NETCoreで使用する証明書を作成する。aspnetcore-https.jsを開くbaseFolderがWindowsとLinuxで違う場所に作成するために分岐をしていて、面倒くさいので、ClientAppにフォルダを作成してしまう。ClientApp\.devcertを作成する。
    ``` diff_javascript
    -  const baseFolder =process.env.APPDATA !== undefined && process.env.APPDATA !== ''
    -  ? `${process.env.APPDATA}/ASP.NET/https`
    -  : `${process.env.HOME}/.aspnet/https`;
    +  const baseFolder = path.join(__dirname, '.devcert');
    ```
13. .gitignoreに/.devcert/*を無視するように設定
14.  package.jsonを編集してnpm run dev の前にaspnetcore-https.jsを実行するように変更する。`"predev": "node aspnetcore-https",`をscriptsに追加
     ``` diff_json
     +     "predev": "node aspnetcore-https",
     ```
15. vite.config.jsを開きサーバー起動時に証明書の設定とポートの設定を行う。
    ``` js:vite.config.js
    import path from 'path'

    const baseFolder = path.join(__dirname, '.devcert');
    const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
    const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
    const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
    const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
    export default defineConfig({
     plugins: [vue()],
     server: {
         port: 44421,
         https: {
             key: keyFilePath,
             cert: certFilePath
         }
    }})
    ``` 
16. VisualStudioから実行してみる。 SpaPorxyがnpm を起動して、サーバーが立ち上がるブラウザをlocalhos:44421へredirectしているのがわかる。
![Vue実行.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613576/45a9dec8-af8d-94a4-2ce1-aaecabd6d3fc.jpeg)
17. ASP.NET core WebApiを呼び出すために、Proxyの設定をフロントエンドViteに設定する。WebAPIは`https://localhost:7066`で動作しているので、`https://localhost:7066/api` に対してProxyを設定する。vite.config.jsを修正。
    ``` js:vite.config.js
    import path from 'path'

    const baseFolder = path.join(__dirname, '.devcert');
    const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
    const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
    const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
    const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
    export default defineConfig({
     plugins: [vue()],
     server: {
         port: 44421,
         proxy: {
             '^/api': {
                 target: 'https://localhost:7066',
                 changeOrigin: true,
                 secure: false
             }
         },
         https: {
             key: keyFilePath,
             cert: certFilePath
         }
    }})
    ```