Готовим ASP.NET Core: как представить статический контент в виде ресурсов

от автора


Мы продолжаем нашу колонку по теме ASP.NET Core очередной публикацией от Дмитрия Сикорского ( DmitrySikorsky) — руководителя компании «Юбрейнианс» из Украины. В своей статье Дмитрий рассказывает об опыте работы со статическим контентом в виде ресурсов вне основной сборки проекта в ASP.NET Core. Предыдущие статьи из колонки всегда можно прочитать по ссылке #aspnetcolumn — Владимир Юнев

Иногда необходимо, чтобы статический контент (вроде JS-, CSS-файлов или картинок) располагался, например, вне основной сборки веб-приложения в виде ресурсов. В этой небольшой статье я расскажу о двух подходах к решению этой задачи.

Подготовка проекта с ресурсами

Во-первых, нам необходим проект с ресурсами. Для примера, добавим в ресурсы один CSS-файл (который будет делать весь текст на странице красным) и одну картинку. Для этого нам понадобятся сами файлы, а также, примерно следующая строка в файле project.json нашего проекта:

"resource": [ "Styles/**", "Images/**" ] 

Вот и все, теперь после сборки проекта все содержимое папок Styles и Images превратится в ресурсы (очевидно, что можно указать действительно конкретные файлы, а не целые папки, если в этом есть необходимость).

aspnetcolumngithubСовет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub https://github.com/DmitrySikorsky/AspNet5Resources.

Кстати, при добавлении файлов в ресурсы «древовидность» их расположения становится «плоской», и все символы «\» в пути к файлу превращаются в точки. Т. е. информация об исходном расположении утрачивается (учитывая, что имена файлов могут содержать точки). Например, добавленный в ресурсы файл \Styles\test.css в проекте AspNet5Resources.Resources будет иметь следующее имя (регистр имеет значение):

AspNet5Resources.Resources.Styles.test.css

К счастью, нам не понадобится каждый раз писать имя сборки (в данном случае это AspNet5Resources.Resources) при получении контента из ресурсов. Для этого при создании EmbeddedFileProvider оно указывается в качестве базового пространства имен (об этом ниже).

Использование ресурсов

Мы можем использовать контент, добавленный в проект в виде ресурсов, как минимум двумя способами: реализовать все самостоятельно либо воспользоваться готовой реализацией. Оба способа очень просты.

Чтобы реализовать все самостоятельно необходимо добавить в проект, использующий контент из ресурсов (неважно, расположены ресурсы в этой сборке или в другой), контроллер, который будет извлекать запрошенные ресурсы по их именам и записывать их в выходной поток:

public class ResourceController : Controller   {     public ActionResult Index(string name)     {       Assembly assembly = Assembly.Load(new AssemblyName("AspNet5Resources.Resources"));       string fullName = assembly.GetName().Name + "." + name;        if (assembly.GetManifestResourceNames().Contains(fullName))       {         Stream stream = assembly.GetManifestResourceStream(fullName);          return this.Stream(stream);       }        return this.HttpNotFound();     } } 

Для упрощения работы с выходным потоком тут используется наш собственный класс StreamResult, унаследованный от ActionResult:

public class StreamResult : ActionResult {     private Stream stream;      public StreamResult(Stream stream)     {       this.stream = stream;     }      public async override Task ExecuteResultAsync(ActionContext actionContext)     {       HttpResponse httpResponse = actionContext.HttpContext.Response;        await this.stream.CopyToAsync(httpResponse.Body);     } } 

Этого достаточно, чтобы иметь возможность отобразить картинку из ресурсов таким вот образом:

<img src="/resource?name=Images.test.png" /> 

Теперь воспользуемся готовой реализацией «из коробки».

Первым делом нам необходимо реализовать интерфейс IFileProvider, чтобы в результате наш класс (назовем его CompositeFileProvider) умел объединять в себе несколько разных провайдеров. Класс целиком можно посмотреть в исходниках (ссылка в конце статьи), но ключевой момент следующий:

public IFileInfo GetFileInfo(string subpath) {       foreach (IFileProvider fileProvider in this.fileProviders)       {         IFileInfo fileInfo = fileProvider.GetFileInfo(subpath);          if (fileInfo != null && fileInfo.Exists)           return fileInfo;       }        return new NonexistentFileInfo(subpath); } 

Т. е. по сути наш класс при поиске файла просто перебирает все доступные провайдеры в поисках того, в котором этот файл есть.

Чтобы наше приложение могло использовать как физически существующие файлы, так и файлы из ресурсов, создадим экземпляр нашего CompositeFileProvider таким образом:

public IFileProvider GetFileProvider(string path) {       IEnumerable<IFileProvider> fileProviders = new IFileProvider[] { new PhysicalFileProvider(path) };        return new CompositeFileProvider(         fileProviders.Concat(           new Assembly[] { Assembly.Load(new AssemblyName("AspNet5Resources.Resources")) }.Select(a => new EmbeddedFileProvider(a, a.GetName().Name))         )       ); } 

Далее, нам необходимо «зарегистрировать» наш провайдер при старте приложения в классе Startup:

public Startup(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment) {       this.applicationBasePath = applicationEnvironment.ApplicationBasePath;        hostingEnvironment.WebRootFileProvider = this.GetFileProvider(this.applicationBasePath); } 

После этого мы можем использовать контент из ресурсов более привычным способом:

<link href="/Styles.test.css" rel="stylesheet" /> 

Выводы

Лично мне больше нравится второй вариант, т. к. он сильнее напоминает использование обычных файлов (несмотря на то, что файлы извлекаются из ресурсов). Если, например, не использовать точки в названиях файлов, то можно даже заменять в именах ресурсов все точки, кроме последней, символом «\» и таким образом «восстанавливать» исходное расположение и иметь более наглядный URL, но это не так уж важно.

Как и всегда, я подготовил небольшой тестовый проект, чтобы можно было сразу все запустить и увидеть своими глазами: github.com/DmitrySikorsky/AspNet5Resources.

Авторам

Друзья, если вам интересно поддержать колонку своим собственным материалом, то прошу написать мне на vyunev@microsoft.com для того чтобы обсудить все детали. Мы разыскиваем авторов, которые могут интересно рассказать про ASP.NET и другие темы.

Об авторе

Сикорский Дмитрий Александрович
Компания «Юбрейнианс» (http://ubrainians.com/)
Владелец, руководитель
DmitrySikorsky

ссылка на оригинал статьи https://habrahabr.ru/post/277235/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *