问题
The majority of my views are regular <action>.cshtml
files in the normal /Views/<Controller>
folder hierarchy. These are source controlled in git and deployed in the usual "rip and replace" way.
However I also use Razor for rendering templates to create HTML emails, and the email .cshtml templates are specific to each client. I therefore want to be able to load and render them from outside the Application Root folder, so the client-specific customisations are not lost during deployment.
I have successfully created and registered an implementation of the IViewLocationExpander
interface and this works for folders inside the Application Root:
public class EmailViewLocationExpander : IViewLocationExpander
{
protected readonly String _TemplateFolder;
public EmailViewLocationExpander( String TemplateFolder )
{
_TemplateFolder = TemplateFolder.Trim('/');
}
public void PopulateValues( ViewLocationExpanderContext context )
{
}
public IEnumerable<string> ExpandViewLocations( ViewLocationExpanderContext context, IEnumerable<string> viewLocations )
{
var result = new List<String>( viewLocations );
result.Add( $"/{ _TemplateFolder }/Email/{{0}}.cshtml" );
result.Add( $"/{ _TemplateFolder }/Shared/{{0}}.cshtml" );
return result;
}
}
It does not seem to work for paths other than Application Root relative, so e.g. /../Templates
doesn't seem to work.
I also currently rely on having a custom _ViewStart.cshtml
for my email templates, and reading the Mvc source code leads me to think that my only option is to implement a custom IFileProvider
to reference the physical file system outside the current Application Root - is that right and can anyone help me with an example if it is?
回答1:
That seems right to me. The ViewLocationExpander
deals with paths relative to the wwwroot folder so it's not going to be useful for specify paths to files outside of that.
Here is a pretty good article on implementing an IFileProvider
. The articles demonstrates creating and IFileProvider
for accessing views stored in a database but reading them from the file system will be even easier. So it's a great outline of what to consider. https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location
One thing I noted is that the way the way the IFileProvider
works is pretty cool. You register your custom implementation as an option to the RazorViewEngine like below and when the ViewEngine needs to get a file, it asks each FileProvider, in order, for that file until one returns it.
public void ConfigureServices(IServiceCollection services)
{
string customLocation = @"c:\mylocation\"
// Add framework services.
services.AddMvc();
services.Configure<RazorViewEngineOptions>(opts =>
opts.FileProviders.Add(
new MyCustomFileProvider(customLocation)
)
);
}
So in the end, you basically just implement your FileProvider
to provide files for some specific url endpoints. You can obtain them from anyplace you like.
来源:https://stackoverflow.com/questions/42298745/in-asp-net-core-mvc-is-it-possible-to-add-a-view-folder-from-outside-the-projec