I have a class in my Class Library that's doing all sorts of validations and consistency checks for files before returning the result to the Web, and it used to work fine in WebForms with:
System.Web.HttpContext.Current.Server.MapPath("myFilePath here");
But now that I'm doing the same with MVC, the routing is messing up the MapPath.
How can I get the "base" path of the application in the Class Library using MvC?
Use:
HttpContext.Server.MapPath("~/myFilePath here");
I normally end up passing that path from controller to the helper library where it is needed.
Other option is using:
System.Web.Hosting.HostingEnvironment.MapPath("~/Your relative path from root website")
BTW, to use HostingEnvironment.MapPath(), you will need to add reference to System.Web.
Related
Background
I have an application where 3 views utilize html5 offline app functionality. As such, I have an app manifest generated in a razor view. A cut-down version of this view may look like the following:
CACHE MANIFEST
CACHE:
/site.min.css
/site.min.js
In order for the offline app to function correctly, the cached files must exactly match those requested by offline pages within the app. However, I would like to apply a 'cache-busting' version string to the js/css resources referenced in this manifest. For HTML tags, this is supported by the ScriptTagHelper but I have not found any helpers/extension methods which support this for plain URLs (as required in the above manifest).
With reference to this post, I have resolved this by injecting a FileVersionProvider into my manifest view and using the AddFileVersionToPath() method as follows:
#inject FileVersionProvider versionProvider
CACHE MANIFEST
CACHE:
#versionProvider.AddFileVersionToPath("/site.min.css")
#versionProvider.AddFileVersionToPath("/site.min.js")
However, the FileVersionProvider class is in the Microsoft.AspNetCore.Mvc.TagHelpers.Internal namespace which does not fill me with confidence form a maintenance perspective.
Finally, my implementation of the DI setup for this is not exactly ideal (see below). I don't like the fact that I need to make calls to GetService() and surely I should be specifying a specific MemoryCache?
services.AddSingleton<FileVersionProvider>(s =>
new FileVersionProvider(
s.GetService<IHostingEnvironment>()?.WebRootFileProvider,
s.GetService<IMemoryCache>(),
new PathString("") ));
Question
Has anyone had a requirement to create a link to a js/css resource with a version string before? If so, is there a more elegant solution?
Did you try this extension method? It worked for me on ASP.NET Core 2.1 LTS without any depencency injection, just the extension method. Should work in 2.0 as well if you still use it.
I had a similar use-case where SCEditor loads CSS from a path passed by JavaScript.
#{
// Without version hash cache buster, the editor internally cache the request and we don't get the new stylesheet - even not with CTRL + F5
string editorThemePath = this.AddFileVersionToPath("/css/my-editor-theme.css");
}
<script>
sceditor.create(editorTextarea, {
// ...
style: '#editorThemePath'
})
</script>
Generated HTML
<script>
sceditor.create(editorTextarea, {
style: '/css/my-editor-theme.css?v=sRU_qBg7xyQElPLskjekOlvSHdRF2Ap1leTP8ui4CaI',
})
</script>
Keep in mind that this implementation requires paths relative to wwwroot. So in this case, /css/my-editor-theme.css is in wwwroot/css/my-editor-theme.css.
Could be changed if you replace the first parameter of FileVersionProvider from hostingEnvironment.WebRootFileProvider to hostingEnvironment.ContentRootFileProvider. But I havent' tested what are the side effects and security concerns of this, so you should stay on wwwroot if possible since this folder is designed to be public accessable.
I'm trying to make an ASP.NET Core 2.0 application multi-language. I created a resource file under a folder "Resources" called "Resource.it.resx". I set its access modifier to Public and its Namespace to Resources.
After (re)building the solution I cannot see this namespace in the C# code now in the cshtml code.
Is there some other steps to do?
There are two things you need to take into account:
Only a Resource.resx generates a namespace, culture-specific resources such as Resource.it.resx does not generate a namespace. This is the intended behavior.
ASP.NET Core's localization practices suggest not to use resources directly, but rather find the localized strings using IStringLocalizer.
I suggest you to read the fundamentals of ASP.NET Core localization in the official MSDN guide. There you will find examples for localizing strings in Controllers, Views and wherever you need them.
First thing check if your resource namespace is visible in controller,
like Resources.Resource.
I had not and in this situation I have created emtpy resource class, ex. my resources name is ServiceResources.en-US.resx, in same folder I have ServiceResources.cs empty class too.
Check if you have imported your namespace in _ViewImports.cshtml correctly, with IStringLocalizer class.
I have an HtmlHelper extension method in an assembly separate from my MVC application assembly. Within the extension method I would like to get the version number of the MVC application assembly. Is this possible?
The calling assembly is the razor view dynamic assembly so that doesn't help. Is there some object nested within the HtmlHelper that can provide me with the version number of the MVC application assembly? I've been exploring the HtmlHelper class documentation but so far haven't found a solution to my problem.
Thanks!
This is a notoriously evil thing - because unfortunately there's no one specific reliable way to do it.
Since it's an MVC application, however, the chances are that it has a Global.asax.cs - therefore it has a locally defined HttpApplication class.
From within an html helper you can get to this:
public static string AppVersion(this HtmlHelper html)
{
var appInstance = html.ViewContext.HttpContext.ApplicationInstance;
//given that you should then be able to do
var assemblyVersion = appInstance.GetType().BaseType.Assembly.GetName().Version;
//note the use of the BaseType - see note below
return assemblyVersion.ToString();
}
Note
You might wonder why the code uses the BaseType of the application instance, and not simply the type. That's because the Global.asax.cs file is the primary type of the MVC application, but then Asp.Net dynamically compiles another HttpApplication type that inherits from that via the Global.asax.
As I said earlier; this works in most MVC sites because they should all have an application class defined in a Global.asax.cs file by convention (because that's the way the project is set up).
Just in case anyone comes across this, here is what worked for me (MVC5 VS2013). Enter straight into the view:
#ViewContext.HttpContext.ApplicationInstance.GetType().BaseType.Assembly.GetName().Version.ToString();
Just search for the assembly which should be the source for your version number
AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name.Equals("MyDll")).First().GetName().Version.ToString();
I am using Structure Map to load plugins from a child directory.
Both the main app and the plugin reference the FileHelpers dll. FileHelpers has attributes that you put on a class to define what the record is delimited by. These are defined in my plugin. eg.
[Delimited('\t')]
public class Test {
public string name;
}
The FileHelpers utitlity is run from the main app using the class definitions provided by the plugins. If I put the plugin dll in a directory underneath the main application then I get an issue with the FileHelpers library complaining that the attribute cannot be found, however if it is placed next to the main library (same folder), then it works fine.
I have placed some further debug statements into my code and have found that if
var type = typeof(Test);
var attributes = type.GetCustomAttributes(true);
is used and not the specific (the one FileHelpers is using)
var attributes = type.GetCustomAttributes(typeof(DelimitedAttribute), true);
then it finds the custom attributes without any troubles.
I thought this may have been a SM thing but have tried MEF and by doing it using Assembly.Load() and the same thing happens.
I think you are running into the issue described here.
According to the blog post linked in the answer, it looks like the plugin dll would need to be strongly named and fully trusted, otherwise GetCustomAttributes will filter out the DelimitedAttribute. You could try to add the AllowPartiallyTrustedCallers attribute to the plugin assembly.
I have created a utility method that contains some try/catches in it. In those try/catches I need to redirect the customer using an HttpResponse redirect. I can't seem to figure out how to do this outside a web project. This utility class is referenced from my ASP.NET web project and so I'm just abstracting out some of the code into this utility class so I no longer have the request object.
I know I can use HttpWebRequest object for a lot of web related request tasks outside a web project, but could not seem to get any redirect method there to use after putting in a using System.Net; in my utility class.
Top of your file:
using System.Web;
In your function:
HttpContext.Current.Response.Redirect("http://www.mysite.com/redirect");
This way does make your library dependant on the System.Web library, but your code isn't in a web project. It will grab the current HttpContext for the request it's called from. You'll probably want to do some null checking though, if this code is called from outside of a web-context you're going to get a NullReferenceException.
If you're using WCF there's a different context-object you want to use, I just can't remember what it is. OperationContext maybe?
Why not pass the HttpContext into the Utility method?
In general I try to keep dependencies like these out of Utility classes. Refactor your utility method so it returns some indication to the caller to redirect to a different location.
for example, using a AuthenticateResults enum
AuthenticateResults Authenticate(string username, string password)
then perform your redirect based on the return value.
When you are accessing your utility method from a web based project (so it has context), you can use:
var response = HttpContext.Current.Response;
response.Redirect("{url-to-redirect-to}");