ASP.Net Core 2 IFileProvider не выполняется, если на диске уже существует файл cshtml - PullRequest
0 голосов
/ 19 ноября 2018

Я пишу IFileProvider, с помощью которого я намерен загружать контент из базы данных.Я адаптировал этот код ниже, и в качестве подтверждения концепции я просто жестко кодирую строку, если конкретный запрос соответствует «контакту».

На диске у меня есть файл, contact.cshtml в моей папке Страницы.Если я не использую мой новый DatabaseFileProvider, то страница contact.cshtml отрисовывается идеально, как и ожидалось.

Когда я включаю IFileProvider ниже, запрос к contact.cshtml страница действительно переопределяется - опять же, как и ожидалось.

Однако, если попытаться перехватить запрос на файл cshtml, который не существует, мой код фактически никогда не попадет.Например, вы можете видеть в моих комментариях, что если я заменю работу «контакт» на «динамическую», она не будет выполнена.

Я уверен, что это связано с тем, как я переплел мою IFileProvider с MVC по умолчанию PhysicalFileProvider.

Вот мои настройки:

public class DatabaseFileProvider : IFileProvider
    {

        public IFileInfo GetFileInfo(string subpath)
        {
            var result = new DatabaseFileInfo(subpath);
            return result.Exists ? result as IFileInfo : new NotFoundFileInfo(subpath);
        }
    }

public class DatabaseFileInfo : IFileInfo
    {
        private string _viewPath;
        private byte[] _viewContent;
        private DateTimeOffset _lastModified;
        private bool _exists = false;

        public DatabaseFileInfo(string viewPath)
        {
            _viewPath = viewPath;
            GetView(viewPath);
        }
        public bool Exists => _exists;

        public bool IsDirectory => false;

        public DateTimeOffset LastModified => _lastModified;

        public long Length
        {
            get
            {
                using (var stream = new MemoryStream(_viewContent))
                {
                    return stream.Length;
                }
            }
        }

        public string Name => Path.GetFileName(_viewPath);

        public string PhysicalPath => null;

        public Stream CreateReadStream()
        {
            return new MemoryStream(_viewContent);
        }

        private void GetView(string viewPath)
        {
            if (viewPath == null) return;
            if (viewPath.ToLower().IndexOf("_view") != -1) return;

            // PROBLEM: this only works if the file EXISTS on disk ???!!!  Try switching out "contact" (works) with "dynamic" (doesn't work)
            // Not even the breakpoint gets hit?

            if (viewPath.ToLower().ToLower().IndexOf("contact") == -1) return;

            var html = "This is NOT cshtml";

            _viewContent = Encoding.UTF8.GetBytes(html);
            _lastModified = DateTime.Now;
            _exists = true;
        }
    }

`

... и в моем Startup.cs файле:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            services.Configure<RazorViewEngineOptions>(opts => {
                    opts.FileProviders.Clear();
                    opts.FileProviders.Add(new DatabaseFileProvider());
                    opts.FileProviders.Add(Environment.ContentRootFileProvider);

                    /*
                    opts.FileProviders.Clear();
                    opts.FileProviders.Add(new CompositeFileProvider(
                        Environment.ContentRootFileProvider,
                        new DatabaseFileProvider()
                    )); 
                    */
                }
            );

        }

Как видите, я оставил во второй реализации CompositeFileProvider, но проблема остается.Я также изменил порядок провайдеров вокруг.

Я уверен, что делаю что-то глупое - кто-то может указать мне правильное направление?Очень ценится.

1 Ответ

0 голосов
/ 30 апреля 2019

К вашему сведению, в конце концов я нашел ответ, который должен убедиться, что вы реализуете метод IFileProvider.GetDirectoryContents.Я игнорировал это, потому что думал, что это только для облегчения просмотра каталогов , но похоже, что .Net Core предварительно запрашивает все содержимое провайдера при запуске, и этот метод используется, чтобы точно сказать, что вы 'Вы получили.

Полный исходный код прилагается ниже:

public class FoundationEmbeddedResourcesFileProvider : IFileProvider
    {

        private static readonly Assembly FoundationAssembly = typeof(Foundation.UI.Web.Core.Code.Library).GetTypeInfo().Assembly;
        private static readonly List<string> ResourceNames = FoundationAssembly.GetManifestResourceNames().ToList();
        private bool IsPartOfCompositeProvider { get; set; }


        public class FoundationEmbeddedResourceDirectory : IDirectoryContents
        {

            private IEnumerator<IFileInfo> Files { get; set; }

            public FoundationEmbeddedResourceDirectory(Assembly ass, string subPath)
            {
                var subFolderNames = new List<string>();
                var subFilePaths = new List<string>();

                foreach (var resource in ResourceNames) {
                    var filePath = ConvertResourceFormatToFilePath(resource); // e.g. /Pages/Foundation/Views/MainMenu.cshtml

                    // If this item is not in our directory, we break
                    if (!filePath.ToLower().StartsWith(subPath.ToLower())) continue;


                    var subFolders = filePath.Substring(subPath.Length).Split("/").Where(x => !string.IsNullOrWhiteSpace(x)).ToList();

                    // Remove the 'file' component from the directories list
                    subFolders = subFolders.Take(subFolders.Count - 1).ToList();

                    // Direct directory?
                    if (subFolders.Count == 1) {
                        subFolderNames.Add(subFolders[0]);
                        continue;
                    }

                    // Direct file?
                    if (subFolders.Count == 0) {
                        subFilePaths.Add(filePath);
                        continue;
                    }

                }

                var files = new List<IFileInfo>();
                files.AddRange(subFolderNames.Distinct().Select(x => new ResourceStreamResult(ass, x, true)));
                files.AddRange(subFilePaths.Distinct().Select(x => new ResourceStreamResult(ass, x, false)));

                // Add sub folders
                // if (subPath == "/Pages") files.Add(new ResourceStreamResult(ass, "Foundation/Views", true));

                this.Exists = files.Any();
                this.Files = files.GetEnumerator();
            }

            #region Implementation of IEnumerable

            /// <inheritdoc />
            public IEnumerator<IFileInfo> GetEnumerator()
            {
                return this.Files;
            }

            /// <inheritdoc />
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            #endregion

            #region Implementation of IDirectoryContents

            /// <inheritdoc />
            public bool Exists { get; set; }

            #endregion
        }

        public class FoundationEmbeddedResourceChangeToken : IChangeToken
        {
            #region Implementation of IChangeToken

            /// <inheritdoc />
            public IDisposable RegisterChangeCallback(Action<object> callback, object state)
            {
                return null;
            }

            /// <inheritdoc />
            public bool HasChanged
            {
                get { return true; }
            }

            /// <inheritdoc />
            public bool ActiveChangeCallbacks
            {
                get { return false; }
            }

            #endregion
        }

        public class ResourceStreamResult : IFileInfo
        {
            #region Implementation of IFileInfo

            // private Assembly Assembly = null;
            private Stream Stream = null;
            private string ResourceName = "";

            public ResourceStreamResult(Assembly assembly, string filePath, bool isDirectory)
            {
                this.IsDirectory = isDirectory;
                // this.Assembly = assembly;
                this.PhysicalPath = filePath;
                this.ResourceName = ConvertFilePathToResourceFormat(filePath);
                if (!string.IsNullOrWhiteSpace(this.ResourceName)) this.Stream = assembly.GetManifestResourceStream(this.ResourceName);
            }

            /// <inheritdoc />
            public Stream CreateReadStream()
            {
                return this.Stream;
            }

            /// <inheritdoc />
            public bool Exists
            {
                get {
                    if (this.IsDirectory) return IsDirectory;
                    return (this.Stream != null);
                }
            }

            /// <inheritdoc />
            public long Length
            {
                get
                {
                    if (this.Stream == null) return 0;
                    return this.Stream.Length;
                }
            }

            /// <inheritdoc />
            public string PhysicalPath { get; set; }

            /// <inheritdoc />
            public string Name
            {
                get {
                    if (this.IsDirectory) return this.PhysicalPath;
                    return System.IO.Path.GetFileName(this.PhysicalPath);
                }
            }

            /// <inheritdoc />
            public DateTimeOffset LastModified
            {
                get { return DateTime.UtcNow; }
            }

            /// <inheritdoc />
            public bool IsDirectory { get;set; }

            #endregion
        }

        public FoundationEmbeddedResourcesFileProvider(bool isPartOfCompositeProvider)
        {
            this.IsPartOfCompositeProvider = isPartOfCompositeProvider;
        }


        #region Implementation of IFileProvider

        public static string ConvertFilePathToResourceFormat(string filePath)
        {
            var s = filePath.Replace("/", ".");
            if (s.StartsWith(".")) s = s.Substring(1);

            // CSHTML files are prefixed with the /Pages root, but content files (.js etc) are not.  This makes sense because there are different providers for static files and content files
            // BUT, we source all our content files through the /Pages sub directory because we like to nest javascript/css alongside their views
            if (!s.StartsWith("Pages.")) s = "Pages." + s;

            // Prefix with assembly name
            s = "Foundation.UI.Web.Core." + s;

            // Resource names are case sensitive but the incoming request is not necessarily
            s = ResourceNames.FirstOrDefault(x => x.ToLower() == s.ToLower());

            return s;
        }

        public static string ConvertResourceFormatToFilePath(string resourceName)
        {
            var s = resourceName;
            s = s.Replace("Foundation.UI.Web.Core", "");

            s = s.Replace(".", "/");

            // Retain file extension
            var lastSlash = s.LastIndexOf("/");
            if (lastSlash != -1) s = s.Substring(0, lastSlash) + "." + s.Substring(lastSlash + 1);

            return s;
        }

        /// <inheritdoc />
        public IFileInfo GetFileInfo(string subpath)
        {
            var resource = new ResourceStreamResult(FoundationAssembly, subpath, false);

            if (!resource.Exists) {
                // Composite providers expect NULL in order to move on to the next item
                if (this.IsPartOfCompositeProvider) return null;
                return new NotFoundFileInfo(subpath);
            }
            return resource;
        }

        /// <inheritdoc />
        public IDirectoryContents GetDirectoryContents(string subpath)
        {
            return new FoundationEmbeddedResourceDirectory(FoundationAssembly, subpath);
        }

        /// <inheritdoc />
        public IChangeToken Watch(string filter)
        {
            return new FoundationEmbeddedResourceChangeToken();
        }

        #endregion
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...