ASP.NET Core 2.1 Angular 6.0 SPA template - razor page (cshtml) for index instead static page

纵然是瞬间 提交于 2019-12-17 16:26:40

问题


I am trying to migrate my ASP.NET Core 2.0 Angular 5 application with webpack setup to ASP.NET Core 2.1 Angular 6 with Angular-Cli.

Short question:

How can I force parsing razor pages directives from cshtml with Microsoft.AspNetCore.SpaServices.Extensions?

I do not want to use index.html from Angular.json, but index.cshtml.

Long description:

I started a new ASP.NET Core project from Angular template. There are quite a few things that just magically works.

  1. There is an index.html file inside ClientApp/src folder which is automatically served when application starts.
  2. When application runs, before </body> tag from index.html (from first point) several scripts are inserted: inline.bundle.js, polyfills.bundle.js, vendor.bundle.js, main.bundle.js and styles.bundle.css before </head> tag.

I am not sure who inserted those scripts, but I want to insert them to Razor page (cshtml).

I tried to use code from my old project:

<!DOCTYPE html>
<html>
<head>
    <base href="/" />
    <title>@ViewData["Title"]</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
@{string googleAnalyticsId = Html.Raw(ViewData["GoogleAnalyticsId"]).ToString();}
@if (!string.IsNullOrEmpty(googleAnalyticsId))
{
    <script>
        (function(i, s, o, g, r, a, m)
        {
           ...
        })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');

        ga('create', '@googleAnalyticsId', 'auto');
    </script>
}
<script>
    @Html.Raw(ViewData["TransferData"])
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/0.8.2/css/flag-icon.min.css" async defer />
<script src="https://apis.google.com/js/platform.js" async defer></script>
<app>
    <!-- loading layout replaced by app after startupp -->
    <div class="page-loading">
        <div class="bar">
            <i class="sphere"></i>
        </div>
    </div>
</app>
</body>
</html>

And point index options in Angular.json to this index.cshtml:

  "architect": {
    "build": {
      "builder": "@angular-devkit/build-angular:browser",
        "options": {
            "outputPath": "wwwroot/dist",
            "index": "Views/Home/Index.cshtml",   //<----- here

Problem is that content (all @ tags) is not processed by ASP.NET Core but it outputs direct content:

<!DOCTYPE html>
<html>
<head>
    <base href="/" />
    <title>@ViewData["Title"]</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
@{string googleAnalyticsId = Html.Raw(ViewData["GoogleAnalyticsId"]).ToString();}
@if (!string.IsNullOrEmpty(googleAnalyticsId))
{
    <script>
...

but it should be:

<!DOCTYPE html>
<html>
<head>
    <base href="/" />
    <title>Test title</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
    <script>

I want to print some variables (from appsettings.json) to the end html page. Since I am already using ASP.NET Core, Razor page seems good solution.

I also try:
to add new RazorPage, C# content (@ directives) are processed, but no-one is inserting Angular scripts.

Response for AndyCunningham answer:

ng build --prod writes this files:

<script type="text/javascript" src="runtime.b38c91f828237d6db7f0.js"></script><script type="text/javascript" src="polyfills.2fc3e5708eb8f0eafa68.js"></script><script type="text/javascript" src="main.16911708c355ee56ac06.js"></script> 

to index.html. How to write this to razor page.


回答1:


it's and old question, but I hope the answer help someone

Well, is difficut make that webpack insert the scripts tag in you index.cshtml, but you can include the tags in the index.cshtml like

@{
    ViewData["title"] = "I'am from cshtml";
}
<app-root>Loading...</app-root>
<script type="text/javascript" src="/runtime.js"></script>
<script type="text/javascript" src="/polyfills.js"></script>
<script type="text/javascript" src="/styles.js"></script>
<script type="text/javascript" src="/vendor.js"></script>
<script type="text/javascript" src="/main.js"></script>

then you can router your mvc like

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller}/{action=Index}/{id?}");

    routes.MapRoute(
        name: "spa-fallback",
        template: "{*url:regex(^((?!.css|.map|.js|sockjs).)*$)}",
        defaults: new { controller = "Home", action = "Index" });

});

See that all the routes that not correcponding with {controller}{action=Index}/{id?} fall in "spa-fallback" that say that if the file not have .css or .map or .js or .sockjs serve your Home/Index. It's necesary the "regex" for .NET serve the script files necesarys.

The last step is configure your angular.json to maintaine the name of the scripts using outputHashing:all

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "ClientApp": {
      ...
      "architect": {
        "build": {
            ...
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all", //<--add this line
                  ...
            },
            ....
  },
  "defaultProject": "ClientApp"
}

At last we can check if an url is valid or not (else any url server the index page) you can use in startup.cs some like

app.Use(async (context, next) =>
{
    string path = context.Request.Path.Value.Substring(1);
    bool encontrado = path.EndsWith(".js") || path.EndsWith(".css") || path.EndsWith(".map") || path.StartsWith("sockjs");
    if (!encontrado)
    {
        ..use your code to check the url...
    }
    if (encontrado)
        await next.Invoke(); //<--continue
    else
    {   //send to an error404 page
        context.Response.ContentType = "text/html";
        await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "error404.html"));
    }
});

NOTE: I put the href of the scripts in absolute path, e.g src="/runtime.js", well in package json we can have several scripts like

"scripts": {
    "ng": "ng",
    "start": "ng serve --base-href=/ --serve-path=/",
    "start:fr": "ng serve --base-href=/fr-FR/ --configuration=fr --serve-path=/fr-FR",
    "build": "ng build",
    ...
  },

If we use --serve-path=fr-FR, the href of script must use this serve-path, e.g. src="/fr-FR/runtime.js"




回答2:


I'm basing this answer on my experience with React SPA extensions, but it should be helpful for Angular as well, as it uses the same ClientApp/index.html middleware mechanism, and the same IApplicationBuilder.UseSpa entrypoint.

The problem is that when you use app.UseSpa(...), the default implementation of this extension configures a middleware that listens for ALL UI requests and fwds them to index.html.

I've built a quick example of how we get around this for our projects: https://github.com/andycmaj/aspnet_spa_without_indexhtml/pull/1/files should show you what needs to change in order to ignore index.html and use a razor view instead.

Basically, i've re-implemented what UseSpa does, but without attaching the middleware that intercepts and fwds to index.html.

Note that the Home razor view still needs to do the more important things that index.html was doing. Most importantly, it needs to include bundle.js served by the SPA static content middleware.




回答3:


You're sort of on the right path, you still need everything you have in your _layout.cshtml but you're not modifying Angular index.html. Instead your Home/Index.cshtml or _layout.cshtml will include the <app-root> element as well as all your angular scripts. You'll likely want to disable hashes from thegenerated angular files and use ASP.NET to invalidate your JS.

This link explains this in detail but I still have some minor issues with it in Angular 6 + ASP.NET Core 2.1: Add Angular 5 CLI project to ASP.Net Core 2.0 project



来源:https://stackoverflow.com/questions/50815456/asp-net-core-2-1-angular-6-0-spa-template-razor-page-cshtml-for-index-instea

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!