Comment intercepter 404 à l'aide du middleware Owin


moribvndvs

Contexte

Laissez-moi d'abord vous expliquer le contexte. Je travaille sur un projet qui tente de marier un serveur d'arrière-plan qui utilise l'API Web configurée via OWIN - hébergé sur IIS maintenant, mais potentiellement d'autres hôtes pris en charge par OWIN dans le futur - à une interface utilisant AngularJS.

Le frontend AngularJS est un contenu entièrement statique. J'évite complètement les technologies côté serveur telles que MVC / Razor, WebForms, Bundles, tout ce qui a à voir avec le frontend et les actifs qu'il utilise, et je m'en remets plutôt aux dernières et meilleures techniques utilisant Node.js, Grunt / Gulp, etc. .pour gérer la compilation CSS, le regroupement, la minification, etc. schéma ci-dessous).

MyProject.sln
server
  MyProject.Host
     MyProject.Host.csproj
     Startup.cs
     (etc.)
frontend
  MyProjectApp
     app.js
     index.html
     MyProjectApp.njproj
     (etc.)

Donc, en ce qui concerne le frontend, tout ce que j'ai à faire est de demander à mon hôte de servir mon contenu statique. Dans Express.js, c'est trivial. Avec OWIN, j'ai pu le faire facilement en utilisant le middleware Microsoft.Owin.StaticFiles , et cela fonctionne très bien (c'est très élégant).

Voici ma OwinStartupconfiguration:

string dir = AppDomain.CurrentDomain.RelativeSearchPath; // get executing path
string contentPath = Path.GetFullPath(Path.Combine(dir, @"../../../frontend/MyProjectApp")); // resolve nearby frontend project directory

app.UseFileServer(new FileServerOptions
{
    EnableDefaultFiles = true,
    FileSystem = new PhysicalFileSystem(contentPath),
    RequestPath = new PathString(string.Empty) // starts at the root of the host
});

// ensure the above occur before map handler to prevent native static content handler
app.UseStageMarker(PipelineStage.MapHandler);

La prise

Fondamentalement, il héberge simplement tout frontend/MyProjectAppcomme s'il se trouvait à l'intérieur de la racine de MyProject.Host. Alors naturellement, si vous demandez un fichier qui n'existe pas, IIS génère une erreur 404.

Maintenant, comme il s'agit d'une application AngularJS et qu'elle prend en charge html5mode, j'aurai des routes qui ne sont pas des fichiers physiques sur le serveur, mais qui sont gérées comme des routes dans l'application AngularJS. Si un utilisateur devait déposer sur un AngularJS (autre chose que index.htmlou un fichier qui existe physiquement, dans cet exemple), j'obtiendrais un 404 même si cette route pourrait être valide dans l'application AngularJS. Par conséquent, j'ai besoin de mon middleware OWIN pour renvoyer le index.htmlfichier dans le cas où un fichier demandé n'existe pas, et laisser mon application AngularJS déterminer s'il s'agit vraiment d'un 404.

Si vous êtes familier avec les SPA et AngularJS, il s'agit d'une approche normale et simple. Si j'utilisais le routage MVC ou ASP.NET, je pourrais simplement définir la route par défaut vers un contrôleur MVC qui renvoie my index.html, ou quelque chose du genre. Cependant, j'ai déjà déclaré que je n'utilisais pas MVC et que j'essayais de garder cela aussi simple et léger que possible.

Cet utilisateur a eu un dilemme similaire et l'a résolu avec la réécriture d'IIS. Dans mon cas, cela ne fonctionne pas car a) mon contenu n'existe pas physiquement là où le module d'URL de réécriture peut le trouver, donc il revient toujoursindex.html et b) je veux quelque chose qui ne repose pas sur IIS, mais qui est géré dans Middleware OWIN afin qu'il puisse être utilisé de manière flexible.

TL; DNR moi, pour avoir crié à haute voix.

Simple, comment puis-je intercepter un 404 Not Found et renvoyer le contenu de (note: ne pas rediriger) mon FileServer-servé en index.htmlutilisant le middleware OWIN?

moribvndvs

J'ai écrit ce petit composant middleware, mais je ne sais pas s'il est excessif, inefficace ou s'il y a d'autres pièges. Fondamentalement , il faut juste la même FileServerOptionsles FileServerMiddlewareutilisations, la plus importante étant la partie FileSystemque nous utilisons. Il est placé avant le middleware susmentionné et effectue une vérification rapide pour voir si le chemin demandé existe. Sinon, le chemin de la requête est réécrit comme "index.html", et le StaticFileMiddleware normal prendra le relais à partir de là.

Évidemment, il pourrait être nettoyé pour être réutilisé, y compris un moyen de définir différents fichiers par défaut pour différents chemins racine (par exemple, tout ce qui manque à "/ feature1" devrait utiliser "/feature1/index.html", de même avec "/ feature2 "et" /feature2/default.html ", etc.).

Mais pour l'instant, cela fonctionne pour moi. Cela dépend évidemment de Microsoft.Owin.StaticFiles.

public class DefaultFileRewriterMiddleware : OwinMiddleware
{
    private readonly FileServerOptions _options;

    /// <summary>
    /// Instantiates the middleware with an optional pointer to the next component.
    /// </summary>
    /// <param name="next"/>
    /// <param name="options"></param>
    public DefaultFileRewriterMiddleware(OwinMiddleware next, FileServerOptions options) : base(next)
    {
        _options = options;
    }

    #region Overrides of OwinMiddleware

    /// <summary>
    /// Process an individual request.
    /// </summary>
    /// <param name="context"/>
    /// <returns/>
    public override async Task Invoke(IOwinContext context)
    {
        IFileInfo fileInfo;
        PathString subpath;

        if (!TryMatchPath(context, _options.RequestPath, false, out subpath) ||
            !_options.FileSystem.TryGetFileInfo(subpath.Value, out fileInfo))
        {
            context.Request.Path = new PathString(_options.RequestPath + "/index.html");
        }

        await Next.Invoke(context);
    }

    #endregion

    internal static bool PathEndsInSlash(PathString path)
    {
        return path.Value.EndsWith("/", StringComparison.Ordinal);
    }

    internal static bool TryMatchPath(IOwinContext context, PathString matchUrl, bool forDirectory, out PathString subpath)
    {
        var path = context.Request.Path;

        if (forDirectory && !PathEndsInSlash(path))
        {
            path += new PathString("/");
        }

        if (path.StartsWithSegments(matchUrl, out subpath))
        {
            return true;
        }
        return false;
    }
}

Articles connexes


Comment rediriger l'utilisateur du middleware OWIN?

Qerts J'ai un middleware utilisé à des fins d'authentification et j'ai besoin de distinguer les utilisateurs par type s'ils sont internes ou externes. Après cela, je souhaite rediriger les utilisateurs externes vers une page d'erreur personnalisée, car ils ne

Comment intercepter des exceptions à l'aide de lambdas python

Rems En supposant la version de Python> = 3 et en appelant une liste de fonctions. Je voudrais écrire une fonction lambda qui gère les exceptions. Le fait est que cela ne fonctionne pas, quand il y a une exception lancée dans une fonction, le programme retourn

comment implémenter le pipeline Owin à l'aide du noyau Asp.net

Sinapcs en effet, vous ne pouvez pas utiliser des outils tiers tels que OData, Thinktecture Identity Server, ... dans l'application principale asp.net. Alors, comment pouvons-nous utiliser ces fonctionnalités dans les applications principales asp.net? y a-t-il