How to Implement Vue.js with .NET Core 3

You’ve heard a lot about Vue.js and how awesome it is, and now you want to use it with your .NET Core 3. Well, it is far easier than you might think. Let’s start with .NET Core 3 and Vue.js.

Install .NET Core 3 SDK

Download the latest version of this SDK and install it.

This is image title

Let’s start with .NET Core 3 and Vue.js.

Create ASP.NET Core 3 web application project using API template.

This is image title

It takes a few minutes to create an API project. Once completed, go to the package manager console and write the following command. Make sure the selected project is API project. After that, it will create one folder called client-app.

vue create client-app   

This is image title

Add Microsoft.AspNetCore.SpaServices.Extensions package from NuGet Package or just replace the csproj file with below one. So, it will automatically add this package.

<Project Sdk="Microsoft.NET.Sdk.Web">  
  
  <PropertyGroup>  
    <TargetFramework>netcoreapp3.0</TargetFramework>  
    <SpaRoot>client-app\</SpaRoot>  
    <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>  
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>  
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>  
    <IsPackable>false</IsPackable>  
  </PropertyGroup>  
      
  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">  
    
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />  
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />  
      
    <ItemGroup>  
      <DistFiles Include="$(SpaRoot)dist\**" />  
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">  
        <RelativePath>%(DistFiles.Identity)</RelativePath>  
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>  
      </ResolvedFileToPublish>  
    </ItemGroup>  
  </Target>  
    
<ItemGroup>  
    <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.0.0-preview6.19307.2" />  
  </ItemGroup>  
  
  <ItemGroup>  
     
    <Content Remove="$(SpaRoot)**" />  
    <None Remove="$(SpaRoot)**" />  
    <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />  
  </ItemGroup>  
  
  <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">  
      
    <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>  
    
</Project>  

Let us now inject the middleware at startup file and add connection.cs file for port forwrding.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Threading.Tasks;  
using Microsoft.AspNetCore.Builder;  
using Microsoft.AspNetCore.Hosting;  
using Microsoft.AspNetCore.HttpsPolicy;  
using Microsoft.AspNetCore.Mvc;  
using Microsoft.Extensions.Configuration;  
using Microsoft.Extensions.DependencyInjection;  
using Microsoft.Extensions.Hosting;  
using Microsoft.Extensions.Logging;  
using VueDemoWithAsp.NetCore.VueCoreConnection;  
  
namespace VueDemoWithAsp.NetCore  
{  
    public class Startup  
    {  
        public Startup(IConfiguration configuration)  
        {  
            Configuration = configuration;  
        }  
  
        public IConfiguration Configuration { get; }  
  
        // This method gets called by the runtime. Use this method to add services to the container.  
        public void ConfigureServices(IServiceCollection services)  
        {  
            services.AddControllers();  
            // connect vue app - middleware  
            services.AddSpaStaticFiles(options => options.RootPath = "client-app/dist");  
        }  
  
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
        {  
            if (env.IsDevelopment())  
            {  
                app.UseDeveloperExceptionPage();  
            }  
            else  
            {  
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.  
                app.UseHsts();  
            }  
  
            app.UseHttpsRedirection();  
  
            app.UseRouting();  
  
            app.UseAuthorization();  
  
            app.UseEndpoints(endpoints =>  
            {  
                endpoints.MapControllers();  
            });  
  
            // use middleware and launch server for Vue  
            app.UseSpaStaticFiles();  
            app.UseSpa(spa =>  
            {  
                spa.Options.SourcePath = "client-app";  
                if (env.IsDevelopment())  
                {  
                      
                    spa.UseVueDevelopmentServer();  
                }  
            });  
        }  
    }  
}  

The new file should look like below.

using Microsoft.AspNetCore.Builder;  
using Microsoft.AspNetCore.SpaServices;  
using Microsoft.Extensions.DependencyInjection;  
using Microsoft.Extensions.Logging;  
using System;  
using System.Diagnostics;  
using System.IO;  
using System.Linq;  
using System.Net.NetworkInformation;  
using System.Runtime.InteropServices;  
using System.Threading.Tasks;  
  
namespace VueDemoWithAsp.NetCore.VueCoreConnection  
{  
    public static class Connection  
    {  
         
        private static int Port { get; } = 8080;  
        private static Uri DevelopmentServerEndpoint { get; } = new Uri($"http://localhost:{Port}");  
        private static TimeSpan Timeout { get; } = TimeSpan.FromSeconds(60);  
         
        private static string DoneMessage { get; } = "DONE  Compiled successfully in";  
  
        public static void UseVueDevelopmentServer(this ISpaBuilder spa)  
        {  
            spa.UseProxyToSpaDevelopmentServer(async () =>  
            {  
                var loggerFactory = spa.ApplicationBuilder.ApplicationServices.GetService<ILoggerFactory>();  
                var logger = loggerFactory.CreateLogger("Vue");  
  
                if (IsRunning())  
                {  
                    return DevelopmentServerEndpoint;  
                }  
  
                
                var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);  
                var processInfo = new ProcessStartInfo  
                {  
                    FileName = isWindows ? "cmd" : "npm",  
                    Arguments = $"{(isWindows ? "/c npm " : "")}run serve",  
                    WorkingDirectory = "client-app",  
                    RedirectStandardError = true,  
                    RedirectStandardInput = true,  
                    RedirectStandardOutput = true,  
                    UseShellExecute = false,  
                };  
                var process = Process.Start(processInfo);  
                var tcs = new TaskCompletionSource<int>();  
                _ = Task.Run(() =>  
                {  
                    try  
                    {  
                        string line;  
                        while ((line = process.StandardOutput.ReadLine()) != null)  
                        {  
                            logger.LogInformation(line);  
                            if (!tcs.Task.IsCompleted && line.Contains(DoneMessage))  
                            {  
                                tcs.SetResult(1);  
                            }  
                        }  
                    }  
                    catch (EndOfStreamException ex)  
                    {  
                        logger.LogError(ex.ToString());  
                        tcs.SetException(new InvalidOperationException("'npm run serve' failed.", ex));  
                    }  
                });  
                _ = Task.Run(() =>  
                {  
                    try  
                    {  
                        string line;  
                        while ((line = process.StandardError.ReadLine()) != null)  
                        {  
                            logger.LogError(line);  
                        }  
                    }  
                    catch (EndOfStreamException ex)  
                    {  
                        logger.LogError(ex.ToString());  
                        tcs.SetException(new InvalidOperationException("'npm run serve' failed.", ex));  
                    }  
                });  
  
                var timeout = Task.Delay(Timeout);  
                if (await Task.WhenAny(timeout, tcs.Task) == timeout)  
                {  
                    throw new TimeoutException();  
                }  
  
                return DevelopmentServerEndpoint;  
            });  
  
        }  
  
        private static bool IsRunning() => IPGlobalProperties.GetIPGlobalProperties()  
                .GetActiveTcpListeners()  
                .Select(x => x.Port)  
                .Contains(Port);  
    }  
}  

It’s time to run the Vue.js application.

This is image title

Above is the default view which is provided by Vue. I made some changes with my code and finally, the output looks like below GIF.

This is image title

Let’s recap this post. We learned about what Vue.js is, why a developer chooses it, what the process of installation is and the main concept of Vue to implement with .NET Core 3.

Download full source code here

Thank for reading !

#Vuejs #.NET Core 3 #.NET Core

How to Implement Vue.js with .NET Core 3
302.80 GEEK