Process HTML5 client routing rollback in ASP. NET Core, corehtml5
When using client applications built by application frameworks such as Angular, React, and Vue, you always process HTML5 client routing, it will completely process the client routing of pages and components in the browser. Almost completely in the browser...
HTML5 client routing works well on the client, but when it is deeply linked to a site or refreshed in a browser, the client routing has a bad habit and becomes a server HTTP request. The request may not have the server route configured.
In this article, I will discuss how to make ASP. NET Core (or indirect ASP.. NET application) by effectively reconnecting the client application to its route to process these "fake" requests.
Html 5 Client routing?
If you do not know what the HTML5 client routing is, please review it quickly.
The client framework implements their own client routing mechanism so that they can-like server applications-navigate between pages or components.
Angular supports several routing types:
#!/
Hash bond route
The former is an earlier method. It works directly with the HTTP syntax and specifies any URL with#
When the client is triggered and jumps to the "local" URL on the page. The framework can intercept navigation and check the following URL content.#
To determine the route. Hash explosion#!
Used to distinguish between application URLs and common#
.
The advantage of hashes is that they only work. There is no route for server-side bleeding. If you bookmark or refresh the client page, it only works as expected, because the hash logic is executed as part of the local URL parsing in the browser. Very simple, right? It only works.
However, if you have to manually enter the URLs, these URLs are very ugly and not intuitive. This is not a good argument for hash routes, but whether or not they are detrimental to HTML5 routes.
Hash the Bang route in Angular
Angular uses the default HTML5 client route, but it is a simple switch to enable Hashbang routing, rather than HTML5 routing ::
// in app.module.tsproviders : [ .. // make sure you use this for Hash Urls rather than HTML 5 routing { provide: LocationStrategy, useClass: HashLocationStrategy },]
As long as yourouterLink
Use the link URL in the HTML template, androuter.navigate()
When used in Code links, Angular switches automatically switch between the two modes.
HTML5 Routing
HTML5 routing uses a more complex method-it uses the HTML5 Pushstate API to control the route of the client and manage the display in the address bar.
The advantage of this method is that it is relatively easy to use HTML5 APIs and uses standard non-extended routing conventions. When using Web applications and APIs, the URL is simpler and easier to control.
However, HTML5 routing requires the explicit support of the server to correctly understand which routes are server routes and which are customer routes.
No HTML5 Routing Problems processed by the server
The problem is that HTML5 client routing cannot be distinguished from Server Routing.
http://localhost:4200/albums
You can easily use the client URL as the server URL. HTML5 routes work normally when navigation is complete on the client-the application can intercept navigation and route to the corresponding client page when activating a specific route.
If you use a deep link to navigate to the client-driven application, You Can bookmark the page, use the URL to navigate back to the page, or refresh the current activity page, A problem occurs. In both cases, when the browser requests a route, the client application does not run, so the browser requests the route URL from the server. However, the processing description is not set by default./albums
Route, so you will get an error.
If you do not perform any special processing on HTML5 routing settings in the ASP. NET Core application, you will open the error page in the application, or select this default display from Kestrel:
Figure 1-server errors caused by Unprocessed client routes
Fix client routing on the server
So how do you solve this problem?
The client SPA application usually has one or more static pages for starting the application. For a typical Angular application, the page isindex.html
Start the application and start the client route. Most frameworks are smart enough to check the current route at startup and move it to the route for the first access request.
If the client route is triggered from bookmarks, links, or full refresh to the server, you must provideindex.html
And keep the original URL unchanged.
Then, the client application will be self-guided and the internal route will be started to throw you back to the bookmarks/refresh location.
From the server catalog index.html
For this purpose, make sure that the server only provides the content that the server is responsible.
There are several ways to do this:
URL rewriting on the Web server of the host
If you are running ASP. NET Core (or ASP. NET) Applications on mainstream Web servers, the simplest and most effective solution is to rewrite the client URL andindex.html
The content provided by the given URL.
On IIS, you can use the IIS rewrite module to perform this operation. I have introduced this in more detail in a recent blog:
However, here are the relevant IIS rewrite rules:
<rewrite> <rules> <!-- Make sure you have a <base href="/" /> tag to fix the root path or all relative links will break on rewrite --><rule name="AngularJS-Html5-Routes" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_URI}" pattern="api/" negate="true" /> </conditions> <action type="Rewrite" url="wwwroot/index.html" /> </rule> </rules></rewrite>
You can install the UrlRewrite module from any of the following locations:
If you run Docker, nginX, or Apache on Linux, similar Rewrite options can be used there.
Allow ASP. NET Core to process client routes
As mentioned above, I usually use front-end Web servers such as IIS or nginX to handle redirection, but when testing or internal applications, you only need Kestrel to directly provide services for the application. If you want Kestrel to process HTTP traffic directly, you need to process client routing in ASP. NET Core code.
Capture allapp.Run()
Handler
There are many methods available, but I found thatStartup
ClassConfigure()
Method:
// set up whatever routes you use with UseMvc()// you may not need to set up any routes here// if you only use attribute routes!app.UseMvc(routes =>{ routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}");});//handle client side routesapp.Run( async (context) =>{ context.Response.ContentType = "text/html"; await context.Response.SendFileAsync(Path.Combine(env.WebRootPath,"index.html"));});
The key isapp.Run()
The middleware processing program located at the end of the ingress. If the server-side route cannot find a matched route, this general handler will start.
The above code is the simplest thing you can do, just to send the contentindex.html
To the client. If you have multiple static pages and SPA silos, you can add additional logic in them to try to determine which page to load.
Please note that the content is not redirected, but is sent to the existing URL request as an embedded stream, so that the URL of the user request remains unchanged. This ensures that when the user requestshttp://localhost:4200/albums
You go back to the client page insteadindex.html
.
Capture all route handlers
Another method is to use the fully captured MVC route handler in the route definition. This basically picks up any URL that cannot be processed by your MVC routing configuration, and routes it to the route you specified.
Use the catch-all handler to set your MVC route and place this code in yourStartup
ClassConfigure()
Method:
app.UseMvc(routes =>{ // default routes plus any other custom routesroutes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}"); // Catch all Route - catches anything not caught be other routesroutes.MapRoute(name: "catch-all",template: "{*url}",defaults: new {controller = "AlbumViewerApi", action = "RedirectIndex"});});
Then execute the same thing middleware processing program to use:index.html
Use the following code to stream content to the client:
// we need hosting environment for base pathpublic IHostingEnvironment HostingEnv { get; }public AlbumViewerApiController(IHostingEnvironment env){ HostingEnv = env;}[HttpGet]public IActionResult RedirectIndex(){ return new PhysicalFileResult( Path.Combine(HostingEnv.WebRootPath,"index.html"), new MediaTypeHeaderValue("text/html") );}
Catch-All Route does not use Attribute Routing
Make sure that the route you specified for the rollback route does not have the attribute route assigned to it. When I checked it out yesterday, I couldn't get a full route until I[Route("api/RedirectIndex")]
Removed all work from the Controller operation.
SpaServices
SpaServices provides another option,routes.MapSpaFallbackRoute()
Although I have not tried it myself, if you have already. NET Core applications use the Spa service, which may be a simple method to implement this function, including potential support for server pre-rendering.
Summary
HTML5 routing provides a clean URL for the client application, but its price must be supported by the server to make it work. It is not difficult to set the rewrite rules in the Web server of the host or directly in the middleware pipeline or custom route handler of Kestrel, however, you must ensure that this function is explicitly added to each ASP.. NET application.
Although the old Hash Bang routes do not look so clean, they work normally and do not require any server support. For non-public applications or applications that need to support ancient browsers, the hash line is still a feasible way to provide routing without server support.
Finally, if you are using a complete Web server, UrlRewriting is the cleanest and most effective way to process non-API content directly processed by the backend of the non-ASP. NET kernel.
The selection is good. You have several options to provide convenience, clean the website or simply put it in the function. Your choice...