在不经过OWIN的情况下提供静态文件

时间:2016-03-03 09:00:03

标签: c# request owin dbcontext static-files

短: 对于每个请求,都会创建一个新的OWIN上下文,我希望能够针对某些资源类型或路径(图像,css,js)阻止这种情况。

展开: 在我们的应用程序启动中,我们注册了一个dbcontext创建委托,以便每个请求只创建一次dbcontext。

public virtual void Configuration(IAppBuilder app)
{
    app.CreatePerOwinContext(Factory.DbContextCreateDelegate);
}

如果客户端请求样式表,则将创建OWIN上下文,因此还将创建新的dbcontext。我希望能够根本不创建OwinContext,或者至少能够阻止某些请求类型/路径执行某些“on create”回调。

或者,正如我所知,为什么(部分)“禁用”OWIN的方法会导致问题,我想听听最佳做法是什么?如何在不为每个请求创建数据库上下文的情况下提供静态文件? (这里的小说法是我们的​​静态文件是使用虚拟路径提供程序提供的嵌入式资源...“普通”静态文件也会出现问题。

背景:昨天我开始注意到我们应用程序的某些部分偶尔都没有加载。有时它是单个图像,有时是整个CSS文件。经过一些调查后,我发现有些请求丢失了http 500错误,抛出的异常通常是SQL连接超时(但也有其他异常)。

虽然我们当然正在尝试修复这些例外情况。我确实认为,当客户端请求单个图像时,我们的应用程序设置数据库连接完全是胡说八道...这是单个页面请求的大约10个数据库连接???

对我而言似乎是一个明显的问题,但我昨天一直在谷歌搜索,没有找到任何接近解决方案或解决方法。我错过了什么堆栈?

编辑:我刚试过一种方法,我实际上并没有创建dbcontext而是创建了一个存根。事后看来,这显然不是这个问题的解决方案,因为OwinContext试图继续它的进程,并且当它试图使用该存根dbcontext从数据库中获取用户时将严重失败。 dbcontext不是问题,我需要完全绕过Owin ......我想......

1 个答案:

答案 0 :(得分:11)

Microsoft.Owin.StaticFiles 救援!

我仍然想知道为什么MVC OWIN模板应用程序默认不启用它。但是对于大多数情况,在OWIN中启用静态文件只需要一行代码。

基本情景

设置Owin将文件夹及其内容视为静态文件:

public virtual void Configuration(IAppBuilder app)
{
    app.UseStaticFiles("/PathToYourStaticFilesFolder");
}

有嵌入资源的情景

不幸的是,对于我来说,我们使用VirtualPathProvider的实现将大部分静态内容作为嵌入资源提供。幸运的是,这也相对容易实现,但它需要在VirtualPathProvider周围编写一个包装器来实现OWIN所需的IFileSystem和IFileInfo接口。

我最终解决方案的代码的相关部分,而不是发布整个VirtualPathProvider,因为有大量的在线示例。

VirtualPathProvider周围的包装器:

/// <summary>
/// Represents a virtual file system.
/// A wrapper around <see cref="MyCustomVirtualPathProvider"/> implementing
/// IFileSystem for use in Owin StaticFiles.
/// </summary>
public class VirtualFileSystem : IFileSystem
{
    /// <summary>
    /// Locate the path in the virtual path provider
    /// </summary>
    /// <param name="subpath">The path that identifies the file</param>
    /// <param name="fileInfo">The discovered file if any</param>
    /// <returns>
    /// True if a file was located at the given path
    /// </returns>
    public bool TryGetFileInfo(string subpath, out IFileInfo fileInfo)
    {
        MyCustomVirtualPathProvider virtualPathProvider = 
            (MyCustomVirtualPathProvider) HostingEnvironment.VirtualPathProvider;

        if (!virtualPathProvider.FileExists(subpath))
        {
            fileInfo = null;
            return false;
        }

        try
        {
            EmbeddedResourceVirtualFile virtualFile = 
                (EmbeddedResourceVirtualFile) virtualPathProvider.GetFile(subpath);

            fileInfo = new EmbeddedResourceFileInfo(virtualFile);

            return true;
        }
        catch (InvalidCastException)
        {
            fileInfo = null;
            return false;
        }
    }

    /// <summary>
    /// Not used in our implementation
    /// </summary>
    /// <param name="subpath"></param>
    /// <param name="contents"></param>
    /// <returns></returns>
    public bool TryGetDirectoryContents(string subpath, out IEnumerable<IFileInfo> contents)
    {
        throw new NotImplementedException();
    }
}

嵌入资源的包装器:

/// <summary>
/// Represents the file info of an embedded resource
/// </summary>
public class EmbeddedResourceFileInfo : IFileInfo
{
    /// <summary>
    /// Return file contents as readonly stream. Caller should dispose stream when complete.
    /// </summary>
    /// <returns>
    /// The file stream
    /// </returns>
    public Stream CreateReadStream()
    {
        return virtualFile.Open();
    }

    /// <summary>
    /// The length of the file in bytes, or -1 for a directory info
    /// </summary>
    public long Length => virtualFile.Length;

    /// <summary>
    /// The name of the file
    /// </summary>
    public string Name => virtualFile.Name;

    /// <summary>
    /// When the file was last modified
    /// </summary>
    public DateTime LastModified => virtualFile.LastModified;

    /// <summary>
    /// Returns null as these are virtual files
    /// </summary>
    public string PhysicalPath => null;

    /// <summary>
    /// True for the case TryGetDirectoryContents has enumerated a sub-directory
    /// </summary>
    public bool IsDirectory => virtualFile.IsDirectory;

    private readonly EmbeddedResourceVirtualFile virtualFile;

    /// <summary>
    /// Construct using a <see cref="EmbeddedResourceVirtualFile"/>
    /// </summary>
    /// <param name="virtualFile"></param>
    public EmbeddedResourceFileInfo(EmbeddedResourceVirtualFile virtualFile)
    {
        this.virtualFile = virtualFile;
    }
}

最后,设置Owin以使用我们的虚拟文件系统:

public virtual void Configuration(IAppBuilder app)
{
    var staticFilesOptions = new StaticFileOptions
    {
        FileSystem = new VirtualFileSystem()
    };
    app.UseStaticFiles(staticFilesOptions);
}