How to handle routes by VueJS in ASP.NET core using Endpoints?
Asked Answered
C

2

9

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
        }
    ]
})
Comical answered 28/9, 2019 at 17:39 Comment(3)
Hi, did you figure out how to solve this?Lcm
@FrodeLillerud yes, see this repoComical
try this repo github.com/SoftwareAteliers/asp-net-core-vue-starter.Marking
M
1

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.

Marking answered 16/10, 2019 at 7:12 Comment(0)
K
0

I added a call to the MapFallbackToFile() method available in .NET 6 to solve my Vue3 Routing issue. (Using vue-router version 4.) Any route Url not found in my Controllers maps back to the default Index action in the Default controller (which is essentially the same as the Vue app's index.html SPA landing page). This way route urls defined in Vue will work properly.

app.MapFallbackToController("Index", "Default"); \\ Program.cs

Rather than mapping back to a controller, you can also map back to the index.html file described in and described in this answer.

app.MapFallbackToFile("index.html");

Kanya answered 17/1, 2023 at 21:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.