当前位置: 首页 > 编程语言 > C#

如何使用Owin中间件拦截404分享

时间:2023-04-10 11:36:58 C#

如何使用Owin中间件拦截404背景首先我来介绍一下背景。我正在开发一个项目,该项目尝试使用通过IIS上托管的OWIN配置的WebAPI组合后端服务器,但将来可能会有其他OWIN支持的主机-使用AngularJS的前端。AngularJS前端完全是静态内容。我完全避免使用服务器端技术,如MVC/Razor、WebForms、Bundles,以及任何与前端相关的技术及其使用的资产,而是遵从最新最好的技术,如Node.js、Grunt/Gulp等。CSS编译、捆绑、缩小等。由于我不会在这里深入的原因,我将前端和服务器项目保留在同一个项目中的不同位置(而不是将它们全部直接放在主机项目中(参见下面的原始图像)。MyProject.sln服务器MyProject.HostMyProject.Host.csprojStartup.cs(etc.)frontendMyProjectAppapp.jsindex.htmlMyProjectApp.njproj(etc.)就前端而言,我需要做的就是让我的主机在Express中提供我的静态内容.js,这是微不足道的。使用OWIN,我能够使用Microsoft.Owin.StaticFiles中间件轻松完成此操作,并且效果很好(非常灵活)。这是我的OwinStartup配置:stringdir=AppDomain.CurrentDomain.RelativeSearchPath;//获取执行路径stringcontentPath=Path.GetFullPath(Path.Combine(dir,@"../../../frontend/MyProjectApp"));//解析附近的前端项目目录app.UseFileServer(newFileServerOptions{EnableDefaultFiles=true,FileSystem=newPhysicalFileSystem(contentPath),RequestPath=newPathString(string.Empty)//从主机的根开始});//确保上述发生在地图处理程序之前,以防止本机静态内容处理程序app.UseStageMarker(PipelineStage.MapHandler);Capture基本上它只是在frontend/MyProjectApp托管所有内容,就好像它在MyProject.Host的根目录中一样。自然地,如果您请求一个不存在的文件,IIS将生成一个404错误。现在,由于这是一个支持html5模式的AngularJS应用程序,我将在服务器上有一些不是物理文件的路由,但在AngularJS应用程序中作为路由处理。如果用户要放入AngularJS(除index.html之外的任何内容或实际存在的文件,在这种情况下),即使路由在AngularJS应用程序中有效,我也会得到404。因此,如果请求的文件不存在,我需要我的OWIN中间件返回index.html文件,并让我的AngularJS应用程序确定它是否真的是404。如果您熟悉SPA和AngularJS,这是一种正常且直接的方法.如果我使用的是MVC或ASP.NET路由,我可以将默认路由设置为返回我的index.html或类似内容的MVC控制器。但是,话虽如此,我并没有使用MVC,但我正在努力使它尽可能简单和轻量级。该用户有类似的困境,并通过重写IIS解决了这个问题。在我的例子中,它不起作用,因为a)我的内容实际上并不存在于重写URL模块可以找到它的地方,所以它总是返回index.html和b)我想要一些不依赖于IIS的东西,但是它在OWIN中间件中处理,可以灵活使用。TL;DNR我,大声喊出来。很简单,我如何拦截404NotFound并使用OWIN中间件返回我的FileServer服务的index.html的内容(注意:没有重定向)?如果您使用的是OWIN,您应该能够使用它:usingAppFunc=Func,//EnvironmentTask>;//完成publicstaticclassAngularServerExtension{publicstaticIAppBuilderUseAngularServer(thisIAppBuilderbuilder,stringrootPath,stringentryPath){varoptions=newAngularServerOptions(){FileServerOptions=newFileServerOptions(){EnableDirectoryBrowsing=false,FileSystem=newPhysicalFileSystem(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,rootPath))},EntryPath=newPathString(entryPath)};.UseDefaultFiles(选项.FileServerOptions.DefaultFilesOptions);returnbuilder.Use(newFunc(next=>newAngularServerMiddleware(next,options).Invoke));}}publicclassAngularServerOptions{publicFileServerOptionspublicFileServerOptions;字符串获取;字符串获取;入口路径{得到;放;}publicboolHtml5Mode{get{返回EntryPath.HasValue;}}公共昂ularServerOptions(){FileServerOptions=newFileServerOptions();EntryPath=PathString.Empty;}}publicclassAngularServerMiddleware{privatereadonlyAngularServerOptions_options;私人只读AppFunc_next;私有只读静态文件中间件_innerMiddleware;publicAngularServerMiddleware(AppFuncnext,AngularServerOptionsoptions){_next=next;_选项=选项;_innerMiddleware=newStaticFileMiddleware(接下来,options.FileServerOptions.StaticFileOptions);}publicasyncTaskInvoke(IDictionaryarg){await_innerMiddleware.Invoke(arg);//如果status4代码为4//并且需要支持angularhtml5mode,则路由到根路径if((int)arg["owin.ResponseStatusCode"]==404&&_options.Html5Mode){arg["owin.RequestPath"]=_options.EntryPath.Value;等待_innerMiddleware.Invoke(arg);JavierFigueroa提供的解决方案确实适用于我的项目我程序的后端是一个OWIN自托管Web服务器,我使用启用了html5Mode的AngularJS作为前端。我尝试了许多不同的方式来编写IOwinContext中间件,但在我找到这个之前,它们都不起作用,它终于起作用了!感谢您分享此解决方案。由JavierFigueroa提供的解决方案,或者是否还有其他陷阱。基本上它只采用与FileServerMiddleware使用相同的FileServerOptions,最重要的部分是我们正在使用的FileSystem。它位于上述中间件之前,并快速检查请求的路径是否存在。如果不是,请求路径将被重写为“index.html”,普通的StaticFileMiddleware将从那里接管。显然它可以被清理以供重用,包括为不同的根路径定义不同的默认文件的方法(例如,从“/feature1”请求的任何内容都应该使用“/feature1/index.html”,同样使用“/feature2”和“/feature2”/default.html”等)。但就目前而言,这对我有用。显然这依赖于Microsoft.Owin.StaticFiles。公共类DefaultFileRewriterMiddleware:OwinMiddleware{privatereadonlyFileServerOptions_options;//////使用指向下一个组件的可选指针实例化中间件。/////////publicDefaultFileRewriterMiddleware(OwinMiddlewarenext,FileServerOptionsoptions):base(next){_options=options;}#regionOwinMiddleware的覆盖//////处理单个请求。/////////publicoverrideasyncTaskInvoke(IOwinContextcontext){IFileInfofileInfo;PathString子路径;如果(!TryMatchPath(context,_options.RequestPath,false,outsubpath)||!_options.FileSystem.TryGetFileInfo(subpath.Value,outfileInfo)){context.Request.Path=newPathString(_options.RequestPath+"/index.html");}awaitNext.Invoke(context);}#endregioninternalstaticboolPathEndsInSlash(PathStringpath){returnpath.Value.EndsWith("/",StringComparison.Ordinal);}内部静态boolTryMatchPath(IOwinContext上下文,PathStringmatchUrl,boolforDirectory,outPathStringsubpath){varpath=context.Request.Path;如果(forDirectory&&!PathEndsInSlash(path)){path+=newPathString("/");}if(path.StartsWithSegments(matchUrl,outsubpath)){returntrue;}返回假;JavierFigueroa给出的答案在这里有效并且非常有用!感谢那!但是,它有一个奇怪的行为:只要它不存在(包括入口文件),它就会运行下一个管道两次例如,当我通过UseHtml5Mode应用实现时,以下测试失败:[Test]publicasyncTaskShouldRunNextMiddlewareOnceWhenNothingExists(){//ARRANGEinthitCount=0;varserver=TestServer.Create(app=>{app.UseHtml5Mode("test-resources","/does-not-exist.html");app.UseCountingMiddleware(()=>{hitCount++;});});using(server){//ACTawaitserver.HttpClient.GetAsync("/does-not-exist.html");//断言Assert.AreEqual(1,hitCount);我上面的测试的一些注释,如果有人感兴趣的话:我用来使上面的测试通过的实现如下:namespaceFoo{usingAppFunc=Func,Task>;公共类Html5ModeMiddleware{privatereadonlyHtml5ModeOptionsm_Options;私有只读静态文件中间件m_InnerMiddleware;私有只读静态文件中间件m_EntryPointAwareInnerMiddleware;publicHtml5ModeMiddleware(AppFuncnext,Html5ModeOptionsoptions){if(next==null)thrownewArgumentNullException(nameof(next));if(options==null)thrownewArgumentNullException(nameof(options));m_Options=选项;m_InnerMiddleware=newStaticFileMiddleware(接下来,options.FileServerOptions.StaticFileOptions);m_EntryPointAwareInnerMiddleware=newStaticFileMiddleware((environment)=>{varcontext=newOwinContext(environment);context.Request.Path=m_Options.EntryPath;returnwarem_InnerMiddle(vironment);},options.FileServerOptions.StaticFileOptions);}publicTaskInvoke(IDictionaryenvironment)=>m_EntryPointAwareInnerMiddleware.Invoke(environment);扩展很相似:以上是C#学习教程:HowtouseOwinmiddlewaretointerceptallcontentsharedby404,如果对你有用,需要进一步了解C#学习教程,希望你会更多关注---namespaceOwin{usingAppFunc=Func,Task>;publicstaticclassAppBuilderExtensions{publicstaticIAppBuilderUseHtml5Mode(thisIAppBuilderapp,stringrootPath,stringentryPath){if(app==null)thrownewArgumentNullException(nameof(app));}如果(rootPath==null)thrownewArgumentNullException(nameof(rootPath));如果(entryPath==null)抛出新的ArgumentNullException(nameof(entryPath));var选项=新的Html5ModeOptions{EntryPath=newPathString(entryPath),FileServerOptions=newFileServerOptions(){EnableDirectoryBrowsing=false,FileSystem=newPhysicalFileSystem(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,rootPath))}};app.UseDefaultFiles(options.FileServerOptionDefaultFilesOptions);返回app.Use(newFunc(next=>newHtml5ModeMiddleware(next,options).Invoke));}}}本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如有转载请注明出处: