I'm having trouble serving up static content such as JavaScript in Nancy.
For example using the self hosting sample I have added a test.js to the Views folder and added a
<script type="text/javascript" src="test.js"></script>
tag to the staticview.html page. If I view this page in the browser the JavaScript is executed correctly.
However when I run the sample the JavaScript is not executed. If I view the page in FireBug I see that I'm getting a 404 error for test.js.
I've tried adding
Get["{file}"] = p =>
{
string path = string.Format("Views/{0}", p.file);
return Response.AsJs(path);
};
and when I set a break point and execute Response.AsJs(path) in the immediate window I get a StatusCode of NotFound
I've also tried adding a StaticContentConvention such as
protected override void ConfigureConventions(NancyConventions conventions)
{
base.ConfigureConventions(conventions);
conventions.StaticContentsConventions.Add(
StaticContentConventionBuilder.AddDirectory("/", "Views"));
conventions.StaticContentsConventions.Add(
StaticContentConventionBuilder.AddDirectory("Views", "Views"));
}
What am I doing wrong?
You can configure static content using NancyConventions. Using the code from the following bootstrapper you can place all of your static contents (css/js/html/etc) inside a folder named "static" at the root of your application.
namespace Application
{
public class ApplicationBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureConventions(NancyConventions nancyConventions)
{
nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("Static", #"Static"));
base.ConfigureConventions(nancyConventions);
}
}
}
After this is done you can access static content such as scripts
<script type="text/javascript" src="/static/test.js"></script>
or css
<link rel="stylesheet" type="text/css" href="/static/styles.css">
You do not have to configure any conventions if you do not have special reasons.
Nancy ... is
shipped with a default convention that will look for files in the
content path of your application.
From NancyFx | Managing static content
I achieved the same by just doing this:
Add a folder in the project called "content", add the static contents there (.js, .xap, .ico, ...)
For each content file, set its properties: Build Action: Embedded Resources; Copy to Output Directory: Copy if Newer.
Change the paths to match the new location, for example:
<script type="text/javascript" src="content/test.js"></script>
Adding just for completeness: If you happen to be running Nancy in self host and running via visual studio debugging, and you find you're getting 404's for all static content requests, you must make sure that the build action is set to "Copy Always" for all your static content files!
If you don't do this then these files will not be copied to your output directory and therefore will not exist, hence 404.
For a self hosted Nancy app, I think you need to mark the files as embedded resources - you do for views. For views you then also need to do this in your bootstrapper:
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
return NancyInternalConfiguration.WithOverrides(
x => x.ViewLocationProvider = typeof (ResourceViewLocationProvider));
}
}
You probably have to do something similar.
Alternatively you should (from memory) use .AsJsFile instead of .AsJs.
First time every sharing a solution online. It took me 4 days to find a quick hack that would work as I run through tutorials and learn nancy. Here is the easy solution:
Make sure you have in your project.json file the right setup:
"buildOptions": {
"emitEntryPoint": true,
"copyToOutput": [ "Views/Car/*" ]
},
Next, go to your CarModule.cs:
Get("/status", _ => View["Car"]);
when you compile the code for the first time your view will work. However, after you edit the html and try to compile again you need this little hack:
Change:
Get("/status", _ => View["Car"]);
to:
Get("/status", _ => View["Car.html"]);
We trick the compiler to think it needs to attach the HTML to the assembly.
I hope this helps noobs like me that can't make much working sense out of the above comments straight from the NacyFx documentation.
Related
We have a DNN module that uses Angular as its client side framework.
I'd like to be able to embed all the resources such as html , js ,css ,images and fonts to my module.(actually our module have more than one dll and every one of them has its own resources so that I don't want to copy all of these resource into main module folder every time I want to make a package)
So far I have tried WebResource.axd which was successful to some extent (Here's what I have done)but then I realized that It is somehow impossible to embed html,images and other stuffs rather than js and css (or it isn't?)
Then I decided to try using VirtualPathProvider and I used this open source project that implements an EmbeddedResourcesVirtualProvider.
I have registered this provider using IRouteMapper interface of DNN. Now that I start testing my project I am getting 404 for all of my resources. I tried to debug the project and put some break points over FileExists ,DirectoryExists and GetFile methods of VirtualProvider but the only virtual path that is being asked from VirtaulProvider is "~/Default.aspx" and nothing else
I would like to ask if it is possible to use VirtualParhProvider with DNN ?
We are using DNN 8.
I think you are over complicating things a bit. If you need a virtual provider for your module to work you are doing it wrong (in my opinion).
A module should be a self-contained package that could be deployed on any DNN installation without having to do anything but install the module.
Normally when you buy or download a free module, it comes in a single zip file with all the necessary files contained in that zip. That could be any type of file (.dll, .js, css, .ascx, .aspx etc) is does not matter as long as it's defined in the .dnn installation file.
You can then link to the files in the ascx of your module.
<script type="text/javascript" src="/DesktopModules/YourModulePath/js/file.js"></script>
or
<img src="/DesktopModules/YourModulePath/images/image.jpg">
With WebResource you can embed anything - images, html, fonts etc., so I would suggest continuing with the approach you've already taken.
I downloaded and installed your module in DDN 8 for testing. So the following assumes that setup.
To embed an image you can do this:
In the library MyFramework:
Add a file called image.png to a new folder \content\images\
Set Build Action to Embedded Resource for this image
Add [assembly: System.Web.UI.WebResource("MyFramework.content.images.image.png", "image/png")] to AssemblyInfo.cs
Add protected string myImageUrl { get; private set; } so we can access the URL in the inheriting class
Add myImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(MyModuleBase), "MyFramework.content.images.image.png"); to your OnInit() method
In the consuming project MyModule:
Add <img src="<%=myImageUrl%>"/> to View.ascx
For HTML and similar content type, you can do basically the same as you have already done for the scripts:
In the library MyFramework:
Add a file called myhtml.html to a new folder \content\html\
(in my file I have: <div style="font-weight: bold;font-size: x-large">Some <span style="color: orange">HTML</span></div>)
Set Build Action to Embedded Resource for the html
Add [assembly: System.Web.UI.WebResource("MyFramework.content.html.myhtml.html", "text/html")] to AssemblyInfo.cs
Add protected string MyHtmlUrl { get; private set; } so we can access the HTML in the inheriting class
Add:
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "MyFramework.content.html.myhtml.html";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
using (StreamReader reader = new StreamReader(stream))
{
MyHtmlUrl = reader.ReadToEnd();
}
}
In the consuming project MyModule:
Add <%=MyHtmlUrl%> to View.ascx
I'm testing localization in Nancy and able to get it to work using EMBEDDED resource files but the issue is I don't want embedded resource files because I want them to be allowed to be edited via the GUI or using the file (if I go the DB route or setting the resource file as "content").
According to the doucmentation you should be able to override it to support using a database but I'm unable to get this to work (https://github.com/NancyFx/Nancy/wiki/Localization):
public class ResourceManager : ResourceBasedTextResource
{
public ResourceManager(IResourceAssemblyProvider resourceAssemblyProvider) : base(resourceAssemblyProvider)
{
}
public new string this[string key, NancyContext context]
{
get
{
return "HELO!";
}
}
}
This was just me messing around but I was hoping in the Razor view when I did #Text.Localization. it should return "HELO!" for everything... however it is not working
There really isn't a question in your post so I'm going to have to guess a bit and assume that you're not getting any exception but rather you're not seeing the "HELO!" in your view
Simply implementing a new ResourceBasedTextResource class is not enough. This is a core component and as such you are going to explicitly have to tell Nancy to use it. You do this by overriding the InternalConfiguration property of your Bootstrapper and tell Nancy to use your implementation instead
You can see it in the DemoBootstrapper of the demo that is linked from that wiki page https://github.com/NancyFx/Nancy/blob/8970ac9d6c7cf46e6060f0b83117c19fa18085c2/src/Nancy.Demo.Razor.Localization/DemoBootstrapper.cs#L11
Also, if you are not going to use resource files, then you should look into inheriting from ITextResource interface instead. It's a simple interface so it should be straight forward.
HTH
I try to make a little application with Prism and MEF in order to learn how it works. I'm stuck on a fairly frustrating problem.
I would like to have a "Modules" subdirectory in my base app directory where I copy all the module's dll as a post build event.
These modules are MVVM app with View and ViewModel.
My problem is : When I copy my module's dll in the main app directory, the views are displayed in the shell, but when my modules are in the subdirectory, nothing is displayer.
My modules and their parts are found but according to fuslogvw the views can't be found :
* Assembly Binder Log Entry (27/11/2015 # 16:45:28) *
The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable C:\Users\mouarf\Downloads\Prism-Samples-Wpf-master\Prism-Samples-Wpf-master\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe
--- A detailed error log follows.
=== Pre-bind state information ===
LOG: DisplayName = ModuleB.resources, Version=1.0.0.0, Culture=en-US, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = HelloWorld.vshost.exe
Calling assembly : ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\Users\mouarf\Downloads\Prism-Samples-Wpf-master\Prism-Samples-Wpf-master\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources.EXE.
LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources/ModuleB.resources.EXE.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources/ModuleB.resources.DLL.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources.EXE.
LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources/ModuleB.resources.EXE.
LOG: All probing URLs attempted and failed.
I don't know why MEF look in "modules\en-US\", I think it's probably why it doesn't find any views, but I couldn't find how to specify otherwise.
My bootstrapper :
public class Bootstrapper : MefBootstrapper
{
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules");
DirectoryCatalog catalog = new DirectoryCatalog(path, "*.dll");
this.AggregateCatalog.Catalogs.Add(catalog);
}
protected override DependencyObject CreateShell()
{
return this.Container.GetExportedValue<MainWindow>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (MainWindow)this.Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
My modules :
[ModuleExport(typeof(ModuleAModule))]
public class ModuleAModule : IModule
{
IRegionManager _regionManager;
[ImportingConstructor]
public ModuleAModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.RegisterViewWithRegion(RegionNames.RightRegion, typeof(ViewA));
}
}
My views :
/// <summary>
/// Interaction logic for ViewA.xaml
/// </summary>
[Export]
public partial class ViewA : UserControl
{
public ViewA()
{
InitializeComponent();
}
}
My viewmodels :
[Export]
public class ViewAViewModel : BindableBase
{
private string _title = "Module A";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
}
Anyone ?
Edit :
Here's the solution for who whould like to take a look : HelloWorldPrismMef
Edit 2 :
The investigation still goes on, I discovered the really handy mefx ! So my problem seems to be :
[Part] ModuleA.ModuleAModule from: DirectoryCatalog (Path="Modules")
[Primary Rejection]
[Export] ModuleA.ModuleAModule (ContractName="Prism.Modularity.IModule")
[Import] ModuleA.ModuleAModule..ctor (Parameter="regionManager", ContractName="Prism.Regions.IRegionManager")
[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint contract name
ContractName Prism.Regions.IRegionManager
RequiredTypeIdentity Prism.Regions.IRegionManager n'a été trouvée.
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)
Does that mean that I need to Export a IRegionManager class ?
The log you have posted is for an attempt to load a resource .dll, something that MEF will never load (resource .dlls are used to store application resource information, like strings for internationalization). You should look for errors that do not mention resource .dlls.
Also, it seems to me you are attempting to edit the Prism Library HelloWorld example from GitHub. This particular example has tight coupling with ModuleA (by that I mean that ModuleA is used as a project dependency in HelloWorld) and to my knowledge you can not simply move the ModuleA.dll from the main folder to a modules folder and expect it to work.
My suggestion would be to add a new project, set that to output to a modules folder and see if that loads (leaving the ModuleA project alone). Or you could remove the reference from the HelloWorld project and use the post build event.
Now regarding the loading of modules from a directory, in my humble opinion, you are over complicating it. All you need is
AgregateCatalog.Catalogs.Add(new DirectoryCatalog(#".\Modules"));
Or presuming you have a convention that specifies a pattern for module file names that resembles AppName.Modules.[ModuleNameHere].dll (eg: AppName.Modules.LoginModule.dll you could use something like this to load the modules
AgregateCatalog.Catalogs.Add(new DirectoryCatalog(#".\Modules", "AppName.Modules.*Module.dll"));
Although this does not seem the case here, if you ever try to load modules from a zone deemed as untrustworthy, the default security policy is to ignore the module. This would happen if you attempt to run the application over a network connection like Windows Share. For this scenario you need to add these instructions to App.config
<runtime>
<loadFromRemoteSources enabled="true" />
</runtime>
These should be added after <startup /> section.
Hope this helps you.
Edit:
Does that mean that I need to Export a IRegionManager class ?
No, that's just complaining because mefx has not loaded the assembly that exports it (Prism.Wpf.dll I think it is called).
Personally I found mefx.exe to be cumbersome; I prefer the GUI version
Now regarding your code, I took a look at the GitHub repository and made some changes but not that many (had some issues with references with ModuleC so I had to remove and add again Prism.Mef & company):
Removed the PostBuildEvent from Infrastructure project
Changed the PostBuildEvent from the module projects. This needs some explaining:
all macros come appended with the directory delimiter "\" so you do not need to add it (I am reffering to $(OutDir)\Modules => $(OutDir)Modules).
COPY/XCOPY require the destination path to end with a delimiter or the destination path will be intepreted as a destinaiton directory ( $(OutDir)Modules => *$(OutDir)Modules* ).
Destination directory needs to exist (so first command should be MKDIR)
I also commented (lines that start with REM are comments) out the command that copies the .pdb because I do not think it is needed and added the /z flag to XCOPY.
Added ILoggerFacade as a dependency to demonstrate that the modules actually load. If you run the application from the Visual Studio Debugger, you will see some messages in the debug window.
Added <loadFromRemoteSources enabled="true" /> in App.config => <configuration /> => <runtime /> so I can run the app over a mounted partition where the project is stored.
All of this is in the PR.
Now regarding why it will not auto-display the views in the regions, I can not say yet. I will keep investigating during my free time, but you might have better luck asking Brian Lagunas as he is one of the developers of Prism.
Edit 2:
As I was looking at Brian Lagunas's profile I saw he answered this post that luckily solves the issue.
Will also add a PR to GitHub.
I have a fairly typical Nancy configuration, with views being served from locations on the file system.
Under a specific route I wish to serve views which are not stored on the file system, but will be provided in Markdown format from an external service.
I initially approached the idea by considering creating a custom concrete class for IViewLocationProvider which would deal with locating the views from the external service. By my understanding of Nancy (and I could be wrong here), you can only have one IViewLocationProvider, so adding this custom class would mean that I could not get the rest of the pages (for all my other routes) from the file system.
My approach is now to manually call the Nancy.ViewEngines.Markdown.Render method, passing it my Markdown (encapsulated in a Func<TextReader>) and a fake ViewLocationResult. As so:
public class MyModule
{
public MyModule(ExternalServiceAdapter externalService, MarkDownViewEngine viewEngine, IRenderContext renderContext) : base("/MyPath")
{
Get["/{Name}/{Version}/View"] = parameters => SpecialView(viewEngine, renderContext, externalService, (string)parameters.Name, (string)parameters.Version);
}
protected static Response SpecialView(MarkDownViewEngine viewEngine, IRenderContext renderContext, ExternalServiceAdapter externalService, string name, string version)
{
var contents = externalService.GetData(name, version);
var location = new ViewLocationResult(string.Empty, string.Empty, "md", contents);
return viewEngine.RenderView(location, null, renderContext); //FIXME fails when performing Master page substitution in Super Simple View Engine
}
}
Unfortunately the Markdown includes an SSVE #Master['master'] tag which causes the above to fall down at the indicated line with a null reference exception deep within SSVE. (I have a file on the file system which is named master and is in Nancy's default path convention for views - but I don't think it's the presence of the file which is causing the issue)
Is there a better approach to rendering a Nancy view from markdown which is not located on the file system? (yet maintain a file system provider for other routes)
I am building a client/server solution, using an AngularJS Single Page App as the client component and a Self-Host ServiceStack RESTful API as the server component. A single Visual Studio Console Application Project holds HTML and JavaScript files for the AngularJS component, along with C# classes for bootstrapping the ServiceStack AppHost (I have devolved Interface and Service responsibilities to separate Visual Studio Projects).
I have set all HTML and JavaScript files to have a 'Build Action' of 'None' and a 'Copy to Output Directory' of 'Copy if newer'.
Everything is working very well as long as I am prepared to put up with having a '#' in my site URLs. I would like to eliminate this by using HTML5 pushstate URLs.
Effectively this means I need to persuade ServiceStack to serve up my default Single Page App HTML shell page whenever a non-existent route is requested. There is now a FallbackRoute attribute available in ServiceStack which appears to have been added exactly for this purpose.
However, I am unsure how to use it. I have found people asking similar questions here, here and here. But the answers given were all before the new FallbackRoute attribute arrived.
Essentially, I am looking for a simple, yet complete example of how to use the FallbackRoute attribute to ensure any requests to non-existent routes are redirected to a single static HTML page.
The RazorRockstars.Web has an implementation. I'll modify it to use a wildcard path and a default view:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
public string PathInfo { get; set; }
}
public class RockstarsService : Service
{
[DefaultView("Index")]
public object Any(Fallback request)
{
request.PathInfo = base.Request.PathInfo;
return request;
}
// ...
}
Since this is a service it requires a View page (details here) rather than a content page.
In the RockStars example, I can't determine what view would be rendered for the FallBackResponse, but setting the view explicitly should be all you need.
The [DefaultView("Index")] attribute I added to the Any method maps the response to a Views/Index.cshtml file. The Index.cshtml file can be empty but for a template declaration, and the complete markup for your single page app can be in your template file (i.e. _Layout.cshtml)
Without Razor
Read the html into a string and return it, while setting the content type to "text/html" with an attribute, see wiki docs on service return types
public class RockstarsService : Service
{
static string readContents;
[AddHeader(ContentType = "text/html")]
public string Any(Fallback request)
{
// check timestamp for changes for production use
if (readContents == '') {
using (StreamReader streamReader = new StreamReader(pathFromConfigFile, Encoding.UTF8))
{
readContents = streamReader.ReadToEnd();
}
}
return readContents;
}
// ...
}
It turns out it is all very simple with the FallbackRoute functionality, once you work out how to use it properly:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
public class FallBackService : Service
{
public object Any(Fallback request)
{
return new HttpResult(new FileInfo("index.html")) {ContentType = "text/html"};
}
}
Once this is in place, I find 'index.html' is indeed getting served up whenever I try to hit a non-existent route.
Any static files, such as JavaScript and CSS resources, get served up as normal (as long as they have a 'Copy to Output Directory' setting of 'Copy if newer', of course).
This works like a charm with the HTML5 Push-state functionality in AngularJS.