web-dev-qa-db-fra.com

ASP.NET 5 + Angular 2 routage (page de gabarit non reloadée)

Angular 2 beta utilise le routage html5 par défaut. Cependant, lorsque vous accédez à un composant et que l'itinéraire change (par exemple, http: // localhost: 5000/aboutus ) et vous rechargez/actualisez la page, rien n'est chargé.

La question a été soulevée dans cepostégalement. La plupart des réponses disent que si nous allons poursuivre le routage HTML5 dans angular 2, cette question de routage doit être résolue côté serveur. Plus de discussionici.

Je ne suis pas sûr de savoir comment gérer ce problème en utilisant l'environnement de serveur asp.net.

Y at-il des développeurs 2 angulaires qui utilisent également asp.net et rencontrent ce problème?

PS J'utilise ASP.NET 5. Mes Angular 2 routes utilisent des routes MVC.

21
raberana

Le problème que vous voyez est lié à la différence entre le routage angulaire sur le client et le routage côté serveur MVC. Vous obtenez en réalité une erreur 404 Page Not Found car le serveur ne dispose pas d'un contrôleur ni d'une action pour cette route. Je suppose que vous ne gérez pas les erreurs, raison pour laquelle il semble que rien ne se passe.

Lorsque vous rechargez http://localhost:5000/aboutus ou si vous essayez de créer un lien vers cette URL directement à partir d'un raccourci ou en le saisissant dans la barre d'adresse (liaison en profondeur), une requête est envoyée au serveur. ASP.NET MVC essaiera de résoudre cette route et, dans votre cas, tentera de charger la variable aboutusController et d'exécuter l'action Index. Bien sûr, ce n'est pas ce que vous voulez, car votre route aboutus est un composant angulaire.

Ce que vous devez faire est de créer un moyen pour le routeur ASP.NET MVC de renvoyer les URL qui doivent être résolues par Angular au client.

Dans votre fichier Startup.cs, dans la méthode Configure(), ajoutez une route "spa-fallback" aux routes existantes:

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

    // when the user types in a link handled by client side routing to the address bar 
    // or refreshes the page, that triggers the server routing. The server should pass 
    // that onto the client, so Angular can handle the route
    routes.MapRoute(
        name: "spa-fallback",
        template: "{*url}",
        defaults: new { controller = "Home", action = "Index" }
    );
});

En créant un itinéraire général pointant vers le contrôleur et la vue qui charge finalement votre application Angular, cela permet aux URL que le serveur ne gère pas d'être transmises au client pour un routage correct.

16
Brad Rem

Dans votre Startup.cs, ajoutez ceci à la méthode Configure. Cela doit être avant les autres déclarations app.

app.Use(async (context, next) => {
    await next();

    if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) {
        context.Request.Path = "/index.html"; // Put your Angular root page here 
        await next();
    }
});
8
Jaanus

Ma solution préférée consiste à ajouter le code suivant à Global.asax.cs, qui prend en charge le problème de manière très simple et fiable:

     private const string RootUrl = "~/Home/Index";
     // You can replace "~Home/Index" with whatever holds your app selector (<my-app></my-app>)
     // such as RootUrl="index.html" or any controller action or browsable route

     protected void Application_BeginRequest(Object sender, EventArgs e)
        {
            // Gets incoming request path
            var path = Request.Url.AbsolutePath;

            // To allow access to api via url during testing (if you're using api controllers) - you may want to remove this in production unless you wish to grant direct access to api calls from client...
            var isApi = path.StartsWith("/api", StringComparison.InvariantCultureIgnoreCase);
            // To allow access to my .net MVCController for login
            var isAccount = path.StartsWith("/account", StringComparison.InvariantCultureIgnoreCase);
            if (isApi || isAccount)
            {
                return;
            }

            // Redirects to the RootUrl you specified above if the server can't find anything else
            if (!System.IO.File.Exists(Context.Server.MapPath(path)))
                Context.RewritePath(RootUrl);
        }
6
Methodician

Vous devez utiliser ce routage dans ASP.NET MVC

app.UseMvc(routes =>
{
     routes.MapRoute("Default", "{*url}",  new { @controller = "App", @action = "Index" });
});

Ensuite, vous devez configurer SystemJS avec les options basePath

5
ZOXEXIVO

Je n'ai aucune chance d'obtenir 

 routes.MapRoute("Default", "{*url}",  
                  new { @controller = "App", @action = "RedirectIndex" });

travailler. Je reçois toujours un 404 avec n'importe quel itinéraire côté client.

Mettre à jour:
Compris pourquoi la route fourre-tout ne fonctionnait pas: j'avais une route d'attribut définie ([Route("api/RedirectIndex")]) et bien que la route simple soit directement accessible avec la route de secours, elle ne s'est pas déclenchée. En supprimant la route des attributs, cela a fonctionné.

Une autre solution qui semble fonctionner aussi facilement que le gestionnaire d'itinéraire fourre-tout consiste à créer un gestionnaire personnalisé qui se déclenche à la fin du pipeline de middleware dans Configure():

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

});

//handle client side routes
app.Run( async (context) =>
{
        context.Response.ContentType = "text/html";
        await context.Response.SendFileAsync(Path.Combine(env.WebRootPath,"index.html"));

});

En fin de compte, il s’agit bien de la route à tout faire qui envoie simplement index.html par-dessus la demande d’URL existante si aucun autre gestionnaire n’a pris la demande en charge.

Cela fonctionne bien même en combinaison avec IIS Réécrire les règles (dans ce cas, les instructions ci-dessus ne seront tout simplement pas renvoyées.

A écrit un article de blog sur ce sujet:

2
Rick Strahl

La fonctionnalité que vous recherchez est la réécriture d'URL. Il y a deux manières possibles de le gérer. La méthode classique consiste à laisser IIS faire le travail, comme décrit ici:

https://stackoverflow.com/a/25955654/3207433

Si vous ne voulez pas dépendre d'IIS, vous pouvez gérer cela dans le middleware ASP.NET 5, comme indiqué dans ma réponse ici:

https://stackoverflow.com/a/34882405/3207433

2
Bill

Voici deux autres options pour résoudre ce problème. Vous pouvez soit ajouter la stratégie d'emplacement de hachage à votre module d'application.

import { LocationStrategy, HashLocationStrategy } from '@angular/common';

@NgModule({
imports: [.... ],
declarations: [...],
bootstrap: [AppComponent],
providers: [
{
  provide: LocationStrategy,
  useClass: HashLocationStrategy
}
]
})
export class AppModule { }

Cette option ne fonctionnera que pour les parties de votre application Angular2 qui résident sur le contrôleur domestique ASP.

Votre deuxième option consiste à ajouter à votre contrôleur ASP des itinéraires correspondant à ceux de votre application Angular 2 et à renvoyer la vue "Index".

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [ActionName("Angular-Route1")]
    public IActionResult AngularRoute1()
    {
        return View("Index");
    }

    public IActionResult Route2()
    {
        return View("Index");
    }
}
1
Allen Rufolo

Vous pouvez utiliser les deux routages Lorsque vous appelez Home/Index à partir d’un routage angulaire.

écrire 

Home/Index.cshtml
<my-app></my-app>

app.routing.ts
    const routes: Routes = [
        { path: '', redirectTo: '/Home/Index', pathMatch: 'full' },
        { path: 'Home/Index', component: DashboardComponent }
    ]

Donc, quand l'URL sera Home/Index chargera le composant de l’URL active afin de charger le composant du tableau de bord.

0
Engineer

La solution sélectionnée ci-dessus n'a pas fonctionné pour moi. J'ai également obtenu le code 404 après avoir suivi tous les commentaires du T. J'utilise une application angular5 dans une application MVC5. J'utilise la page d'arrivée d'index par défaut comme point de départ pour angular5. Mon application angulaire se trouve dans un dossier nommé mvcroot/ClientApp/mais lors de la création, elle place les fichiers distribués dans mvcroot/Dist/en modifiant un paramètre du fichier .angular-cli.json avec "outDir": "../Dist".

Cette solution a bien fonctionné. 

De cette façon, seuls les itinéraires du répertoire Dist sont pris en compte. Désormais, vous pouvez appuyer sur Actualiser à chaque fois et sur l'itinéraire exact pour les recharges de l'application angular5 tout en restant sur le bon composant. Assurez-vous de mettre la capture tout d'abord. Sur une note de côté, si vous utilisez une authentification de jeton dans votre angular5, enregistrez-le dans window.localStorage (ou tout autre mécanisme en dehors de votre application angular5) car l'actualisation effacera toute mémoire où vous pourrez peut-être stocker votre jeton dans une variable globale. . Cela évite à l'utilisateur de devoir se connecter à nouveau s'il s'actualise.

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Catch All",
                "dist/{*url}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional });

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }


            );
0
RandallTo

As-tu utilisé: 

directives: [RouterOutlet, RouterLink] dans le composant. 

0
Sachin

appliquez la solution de @ ZOXEXIVO puis, dans votre _Layout.cshtml, ajoutez ceci:

<head>
    <base href="/"/>
    .....
</had>
0
jcmordan