app.MapFallbackToFile causes reload the entire SPA site if the URL typed manually - vue.js

I use the latest recommended SPA + .Net Core-based Web APi pattern where the FE referenced to BE, FE serves proxy to BE during development, and app.UseDefaultFiles()serves index.html where the SPA resides during production. This pattern means no proxy middleware is required as it was in opposite direction when the BE serves FE as a proxy.
app.UseDefaultFiles(); <-- Here the site is loaded first time
app.UseStaticFiles();
app.MapControllers();
app.MapFallbackToFile("/index.html"); <-- Here the site is reloaded if URL typed(changed) manually
Client-side routing is the point. Specifically, I use Vue Router and IIS hosting. When the site is already opened, and a user types URL in the browser, it falls down to app.MapFallbackToFile("/index.html") and then Vue router handles the route.
The problem is that the site is always completely reloading when the URL is just changed (let say from mysite.com/a to mysite.com/b) in this scenario, as I would press F5. It's not always necessarily bad but I would like to control it.
The question is: how to get rid of app.MapFallbackToFile("/index.html") and somehow pass the captured URL to the SPA, as it would be naked SPA without backend which now stays in front of frontend.

If have tried Vue Spa with ASP.NET Core 6 minimal setup and it seems for me, that there is no way to achieve what you want.
When user enters or changes the URL address, the browser navigate away from the page and do a GET request to BE (Backend).
Here is the catch-all fallback route required, otherwise the user gets the 404 error from the web server.
I presume you use the HTML5 History Mode. Here is a part from the Vue Router Docs about this problem.
Since our app is a single page client side app, without a proper
server configuration, the users will get a 404 error if they access
https://example.com/user/id directly in their browser. Now that's
ugly.
Not to worry: To fix the issue, all you need to do is add a simple
catch-all fallback route to your server. If the URL doesn't match any
static assets, it should serve the same index.html page that your app
lives in. Beautiful, again!
If somebody yet knows the solution, please post a new answer.
It would be great to know how to do it!

Related

Blazor server side behind reverse proxy 404

I have a blazor server-side app hosted on IIS behind a reverse proxy (using ARR).
I have tried everything I can think of, but I keep getting 404 on
_framework/blazor.server.js
My base href is is set to "/subsite/":
<base href="/subsite/" />
and all my src values are relative like this:
<script src="_framework/blazor.server.js"></script>
<script src="_content/BlazorInputFile/inputfile.js"></script>
<script src="animations.js"></script>
Every other script ref loads fine, EVEN the _content data, but not the blazor.server.js.
I tried the old PathBase trick for MVC apps as well with no success:
if (!env.IsDevelopment()) {
app.Use((context, next) => {
context.Request.PathBase = new PathString("/subsite");
return next();
});
}
Can anyone tell me how to make Blazor realize where to put the blazor.server.js in a reverse proxy scenario?
Did you try the UsePathBase ?
app.UsePathBase("/subsite");
Here is my test result
Please check this article for more
https://www.billbogaiv.com/posts/net-core-hosted-on-subdirectories-in-nginx
From docs.
Rewrite URLs for correct routing
Routing requests for page components in a Blazor WebAssembly app isn't as straightforward as routing requests in a Blazor Server, hosted app. Consider a Blazor WebAssembly app with two components:
Main.razor – Loads at the root of the app and contains a link to the About component (href="About").
About.razor – About component.
When the app's default document is requested using the browser's address
bar (for example, https://www.contoso.com/):
The browser makes a request.
The default page is returned, which is usually index.html.
index.html bootstraps the app.
Blazor's router loads, and the Razor Main component is rendered.
In the Main page, selecting the link to the About component works on the client because the Blazor router stops the browser from making a request on the Internet to www.contoso.com for About and serves the rendered About component itself. All of the requests for internal endpoints within the Blazor WebAssembly app work the same way: Requests don't trigger browser-based requests to server-hosted resources on the Internet. The router handles the requests internally.
If a request is made using the browser's address bar for www.contoso.com/About, the request fails. No such resource exists on the app's Internet host, so a 404 - Not Found response is returned.
Because browsers make requests to Internet-based hosts for client-side pages, web servers and hosting services must rewrite all requests for resources not physically on the server to the index.html page. When index.html is returned, the app's Blazor router takes over and responds with the correct resource.
When deploying to an IIS server, you can use the URL Rewrite Module with the app's published web.config file. For more information, see the IIS section.
Maybe you could try to enable the forward proxy in IIS manager->server node->application request routing cache->proxy->enable.
If you only have one website, you could just add the website to ARR server farm and then it will create the routing rule automatically. It will be convenient to monitor the back-end server with health check.
Is this ARR warning causing my 404?

Single Page Application Routing

Modern single page applications use routing mechanisms which don't have to rely on fragments or additional url parameters, but simply leverage the url path. How does the browser know when to ask the server for a resource and when to ask the single page application for a spa-page controlled by a router? Is there a browser API which makes it possible to take over the control of url handing which is then taken over by e.g. the vue-router or another routing spa library?
In Vue Router (and I assume other libraries/frameworks are the same) this is achieved through the HTML5 history API (pushState(), replaceState(), and popstate) which allows you to manipulate the browser's history but won't cause the browser to reload the page or look for a resource, keeping the UI in sync with the URL.
For example, observe what happens to the address bar when you enter this command in your browser's console
history.pushState({urlPath:'/some/page/on/stackoverflow'},"",'/some/page/on/stackoverflow')
The new URL is even added to your browser's history so if you navigate away from the page and come back to it you'll be directed to the new URL.
Of course all these URLs are non-existent on the server. So to avoid the problem of 404 errors when a user tries to directly access a non-existent resource you'd have to add a fallback route that redirects to your index.html page where your app lives.
Vue Router's HTML5 History Mode
React Router's <BrowserRouter>
How does the browser know when to ask the server for a resource and
when to ask the single page application for a spa-page controlled by a
router?
SPA Frameworks use routing libraries.
Suppose your javascript app is already loaded in the browser. When you navigate to a route that is defined in your routes array, the library prevents an http call to the server and handles it internally in your javascript code. Otherwise the call is forwarded to the server as a GET Http request.
here is an answer that discribes this behaviour with a clear scenario

How to enable offline support when using HTML5 history api

What are the best practices (and how to go about doing it) to support offline mode when using html5 history api for url rewrites?
For example, (hypothetically) I have a PWA SPA application at https://abc.xyz which has internationalization built in. So when I visit this link, the Vue router (which ideally could be any framework - vue, react, angular etc.) redirect me to https://abc.xyz/en.
This works perfectly when I am online (ofcourse, the webserver is also handling this redirect so that app works even if you directly visit the said link).
However, its a different story when I am offline. The service worker caches all resources correctly so when I visit the URL https://abc.xyz everything loads up as expected. However, now if I manually type the URL to https://abc.xyz/en, the app fails to load up.
Any pointers on how to achieve this?
Link to same question in github: https://github.com/vuejs-templates/pwa/issues/188
Yes, this is possible quite trivially with Service Workers. All you have to do is to configure the navigateFallback property of sw-precache properly. It has to point to the cached asset you want the service worker to fetch if it encounters a cache miss.
In the template you posted, you should be good to go if you configure your SWPrecache Webpack Plugin as follows:
new SWPrecacheWebpackPlugin({
...
navigateFallback: '/index.html'
...
})
Again, it is absolutely mandatory that the thing you put inside navigateFallback is cached by the Service Worker already, otherwise this will fail silently.
You can verify if everything was configured correctly by checking two things in your webpack generated service-worker.js:
the precacheConfig Array contains ['/index.html', ...]
in the fetch interceptor of the service worker (at the bottom of the file), the variable navigateFallback is set to the value you configured
If your final App is hosted in a subdirectory, for example when hosting it on Github pages, you also have to configure the stripPrefix and replacePrefix Options correctly.

Proper 404 handling by server with vue-router in history mode

When using history mode in vue-router the documentation is suggesting a pretty dodgy way to get around some of the limitations it has.It suggests a server-side configuration that catches all URLs that could be a client-side route and rewriting to root (/) so the client-side app is delivered. And then another catch-all route to a 404 component in the client-side router if no routes match.
Problem is, this will mean your server is returning 200 OK status codes to crawlers/indexers for basically every URL, specifically ones that don’t technically exist.
My thoughts so far:
Use IIS <rewriteMap> to list the valid client-side route patterns I have and use that for matches instead of a catch-all on everything not a file/dir.
Problem: pain to manage in tandem with client-side routes.
Routes defined in server config and handed to client-side router via an api endpoint for registration
Problem: setting up an API when you just want to host a static app is a pain.
Any other suggestions?

rails angularjs html5mode webrick or nginx rewrite rule configure how?

i have angularjs app with html5mode enabled.
I have tried to run app in IE8 and it seems that URL is being prefixed with #! is it suppose to be - back button works etc..hashbang mode
I have tried to run app in modern browser and it seems that history api also work. All fine.
But if i hit http://localhost:3000/notes directly in address bar in both browsers i get routing error from webrick/rails app. I thought angular will take over of this request and handle it.
I have NOT setup anything on server side as angular guide say:
Using this mode requires URL rewriting on server side, basically you
have to rewrite all your links to entry point of your application
(e.g. index.html)
Is that why i am getting route error from webrick? And if i will able to setup rewriting rule how the hell it works? I thought if i rewrite something like http://localhost:3000/notes -> http://localhost:3000(index if u wish) the "/notes" - where i want to jump in is gone and angular app will never know where to route..
And if there is no way to tell webrick what and how to rewrite. How do you do in your development environment?
Thank you a lot.
You need to configure your server to return the same data for /notes as if it were the / route. Not a redirect (since that will remove notes). Basically you need a catchall route that returns if it doesn't match anything else. Either that or enumerate the known routes (i.e., /, /notes, /other, etc.), and have all of those return the same document. At that point, angular will detect the path and act accordingly.