Am trying to create a VueJS SPA with ASP.NET core in the backend
Since dotnet SDK
version 3.0.100
no longer supports Vue, I created a React app
dotnet new react
and swapped the ClientApp
with a Vue app
rm -rf ClientApp
vue create frontend
Everything works perfectly except for 1 thing
Whenever I launch the app http://localhost:5000 and click on any Vue Router link, say http://localhost:5000/about (it works) but if I refresh I get an expected 404
Cannot GET /about
Same goes if I typed the URL into the browser and access directly
It's worth noting that the default React app doesn't face this problem, I compared everything between the files of the two ASP.NET apps using this tool and found no differences
Now Vue Router handles routes by Javascript because it's using the #!hash behind the scenes, so ASP.NET router tries to match the URL to an AboutController@index which doesn't exist hence the 404
Researching revealed that I should set a wildcard fallback to the SPA router in Startup.cs
, but all the answers I found here, here, here, this question, answer here, this question and this one are using app.UseMvc()
and I don't have that enabled, I am using app.UseEndpoints
instead
I also tried searching GitHub but same results
This Answer recommends I stick to app.UseEndPoints
and Microsoft migration guide too
The Vue Router Documentation talks about this very issue and proposes a solution for .NET apps using web.config
for IIS like the answer here but am running Kestrel server on a Linux machine so this takes no effect
Here's my Startup.cs
file
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
namespace spa
{
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.AddControllersWithViews();
// In production, the Vue files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "frontend/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
{
app.UseExceptionHandler("/Error");
// 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.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "frontend";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "serve:bs");
}
});
}
}
}
I pushed this app to GitHub here to make it easy to reproduce just in case I forgot something
How can I achieve the same effect of this
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute("spa-fallback", "{*anything}", new { controller = "Home", action = "Index" });
});
using app.UseEndpoints
?
Just in case this is relevant, here's my Vue router
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import NotFound from './views/NotFound.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import( /* webpackChunkName: "about" */ './views/About.vue')
},
{
path: '/forecast',
name: 'forecast',
component: () => import( /* webpackChunkName: "forecast" */ './views/Forecast.vue')
},
{
path: '*',
component: NotFound
}
]
})
The Endpoint Routing is the Process by which ASP.NET Core inspects the incoming HTTP requests and maps them to applications executable Endpoint. We define the Endpoint during the application startup. The Routing Module then matches the incoming URL to an Endpoint and dispatches the request to it.
Now Vue Router handles routes by Javascript because it's using the #!hash behind the scenes, so ASP.NET router tries to match the URL to an AboutController@index which doesn't exist hence the 404
We’re also going to see the basics of Vue JS such as conditional statements and data binding. To start, let’s create a new project in visual studio.
Search for Vue in the search bar at the top and then select Standalone JavaScript Vue Template or Standalone TypeScript Vue Template. Give your project and solution a name. When you get to the Additional information window, be sure to check the Add integration for Empty ASP.NET Web API Project option.
Try this template https://github.com/SoftwareAteliers/asp-net-core-vue-starter. It's recently updated and support .Net Core 3.0 and Vue CLI 3.0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With