I'm building an ASP.NET OWIN-based website (essentially an AngularJs SPA app). The solution can be found here:
https://github.com/dsidirop/PetTrackerOAuth/tree/ffa702c6ffa05fb44b332d1961eb8337da0acdae
The solution consists of two projects: one project hosting OWIN/OAuth/WebAPI2 and another one which hosts the SPA (.html, .css, .js files). Both projects have been tweaked to output their files in the same output directory. Thus index.html and the entire folder structure holding .js/.css files ends up in the /bin directory right next to all the .dlls of the OWIN project. My Startup.cs looks like this:
public sealed class Startup
{
public void Configuration(IAppBuilder appBuilder) //0
{
var webApiConfiguration = new HttpConfiguration();
ConfigureOAuth(appBuilder);
var physicalFileSystem = new PhysicalFileSystem(@".\bin");
var options = new FileServerOptions { EnableDefaultFiles = true, FileSystem = physicalFileSystem };
options.StaticFileOptions.FileSystem = physicalFileSystem;
options.StaticFileOptions.ServeUnknownFileTypes = true;
options.EnableDirectoryBrowsing = true;
options.DefaultFilesOptions.DefaultFileNames = new[] { "index.html" };
appBuilder.UseFileServer(options);
WebApiConfig.Register(webApiConfiguration);
appBuilder.UseCors(CorsOptions.AllowAll);
appBuilder.UseWebApi(webApiConfiguration); //1
appBuilder.UseNinjectMiddleware(() => NinjectConfig.CreateKernel.Value).UseNinjectWebApi(webApiConfiguration);
}
//0 the app parameter is an interface to a builder instance which is be used to compose the application for our owin server
//1 the userwebapi extension method is responsible for wiring up the aspnet web api to our owin server pipeline
public void ConfigureOAuth(IAppBuilder app)
{
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions {
Provider = new SimpleAuthorizationServerProvider(),
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30)
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
My web.config looks like so:
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301879
-->
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections>
<appSettings></appSettings>
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" />
<httpModules>
</httpModules>
</system.web>
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<validation validateIntegratedModeConfiguration="false" />
<modules>
</modules>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Cors" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http.WebHost" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<!-- http://www.asp.net/identity/overview/features-api/best-practices-for-deploying-passwords-and-other-sensitive-data-to-aspnet-and-azure -->
<!-- -->
<!-- The configsource attribute replaces the entire <connectionstrings> markup Thus it doesnt leave any room for merging with existing -->
<!-- attributes in the connectionstring element The external connection strings file must be in the same directory as the root web.config -->
<!-- file so provisions must be in place to ensure you dont check it into your source repository -->
<!-- -->
<!-- Using the configsource attribute as shown below prevents visual studio from detecting that the project is using a database when creating -->
<!-- a new web site and thus you wont get the option of configuring the database when you publishing to azure from visual studio -->
<!-- -->
<!-- <connectionStrings><add name="PetTrackerContext" connectionString="provider connection string="user id=SQLDBUSER;password=PASSWORD;data source=pettracker2016.database.windows.net;initial catalog=PetTracker;persist security info=True;MultipleActiveResultSets=True;App=EntityFramework";metadata=res://*/Models.PetTrackerModel.csdl|res://*/Models.PetTrackerModel.ssdl|res://*/Models.PetTrackerModel.msl;provider=System.Data.SqlClient;" providerName="System.Data.EntityClient" /></connectionStrings> -->
<connectionStrings configSource="ConnectionStrings.config">
</connectionStrings>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
</compilers>
</system.codedom>
</configuration>
I've tried to enrich the Startup.cs file using virtually any approach under the sun I could get my hands on in order to have index.html load properly (I'm not using angular in html5 mode btw). However even though index.html gets displayed upon launching the server, all the other resources (.css, .js files) fail to load with a message-spam "404 Resources not found" showing up in the console of the developer-tools of the browser. Some things I've tried:
How to set default static web page for Katana/Owin self hosted app?
https://docs.asp.net/en/latest/fundamentals/static-files.html
How to intercept 404 using Owin middleware
Nothing I've tried so far has worked. So how can one make index.html load properly (with .js/.css resources and all) under OWIN? Any code snippets which resolve this issue cleanly will be highly appreciated. Thanks in advance and sorry if I missed something obvious.