I've set up a Nancy bootstrapper to serve static content from a non-default directory path (it's self hosted Nancy).
Strangely, the following works for the custom View location convention but not either of the js or css static content conventions (and yes, both files and folders exist at these locations!). My attempts at trying to resolve this are further compounded as I haven't figured out how to log errors which occur when static content is not found.
using System;
using System.IO;
using Nancy;
using Nancy.Conventions;
using Nancy.Bootstrapper;
using Nancy.TinyIoc;
namespace MyApp
{
public class ApplicationBootstrapper : DefaultNancyBootstrapper
{
private const string RELATIVE_PATH_TO_SOURCE = #"../static/MyApp/";
protected override void ConfigureConventions(NancyConventions nancyConventions)
{
nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("js", string.Concat(RELATIVE_PATH_TO_SOURCE, "Scripts/")));
nancyConventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("css", string.Concat(RELATIVE_PATH_TO_SOURCE, "Content/")));
this.Conventions.ViewLocationConventions.Add((viewName, model, context) =>
{
return string.Concat(RELATIVE_PATH_TO_SOURCE, "Views/", viewName);
});
this.Conventions.ViewLocationConventions.Add((viewName, model, context) =>
{
return string.Concat(RELATIVE_PATH_TO_SOURCE, "Views/", context.ModuleName, "/", viewName);
});
base.ConfigureConventions(nancyConventions);
}
protected override IRootPathProvider RootPathProvider
{
get
{
return new MyRootPathProvider();
}
}
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
pipelines.OnError += (ctx, ex) =>
{
Console.WriteLine("RootPath : {0}", DebugRootPathProvider.RootPath);
Console.WriteLine("Unhandled error on request: {0} : {1}", ctx.Request.Url, ex.Message); //HACK
Console.WriteLine(ex.StackTrace); //HACK poor man's logging
return null;
};
}
}
public class MyRootPathProvider : IRootPathProvider
{
public static readonly string RootPath;
static MyRootPathProvider()
{
RootPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
}
public string GetRootPath()
{
return RootPath;
}
}
}
The output from Chrome and ProcMon is as follows:
How should I:
Log errors occurring with not found js and css files?
Resolve the 404 errors with the static file conventions?
Instead of logging you can use sysinternals process monitor and look for what files the nancy process (exe or IIS worker process) are attempting to read.
I had problems with serving static files (in my case js files) in a self host environment as well. They were not recognized at all, not even in the default "Content" folder. My solution: I installed the Microsoft StaticFiles NuGet Package.
In the startup class, register a static files folder like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseStaticFiles("/Scripts");
app.MapSignalR();
app.UseNancy();
}
}
This way, all files and subfolders in the "Scripts" folder are served as static files. There is also an overload of the UseStaticFiles() function that lets you map a physical path to a virtual path.
Important here: the call to UseNancy() has to be the very last, otherwise everything after it won't work. I also tried to combine it with SignalR, as you can see above, and there the UseNancy() call had to be at the end as well.
Related
Using the Visual Studio Installer Project, I included the initial setup project in Install and Commit in Custom Actions that will perform the downloading of the cabinet file under the Windows\Temp\Target Folder folder. Consequently, the zip file will be unzipped.
I used async/await for the first time in DownloadCatalog(), but the zip file wasn't properly downloaded, even though the directory was created. I assumed the installing process stopped the downloading process. I then changed it.
I created the installation file without async. Then I ran it, but the result was the same. This code works fine when running it in an independent project. Do you have any suggestions?
namespace IntialSetupApp
{
[RunInstaller(true)]
public partial class IntialInstallApp : System.Configuration.Install.Installer
{
private readonly string temp = #"C:\Windows\Temp\Target Folder\";
private readonly string zipUrl = #"https://thank.you/so.much";
private readonly string catalog = #"C:\Windows\Temp\Target Folder\whateverXML.xml";
public IntialInstallApp()
{
InitializeComponent();
}
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
Directory.CreateDirectory(temp);
DownloadCatalog();
}
private Task DownloadCatalog()
{
try
{
string fileName = Path.Combine(temp, "ZippedCab.cab");
Uri uri = new Uri(url);
using (WebClient client = new WebClient())
{
client.DownloadFile(uri, fileName);
}
UnzipFile(fileName);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return Task.FromResult(true);
}
private Task UnzipFile(string filePath)
{
try
{
CabInfo cab = new CabInfo(filePath);
cab.Unpack(temp);
return Task.FromResult(true);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return Task.FromResult(false);
}
}
}
+Update
With the above code, I created the console project independently, and it created the folder and completed downloading the file. Therefore, it seems that installer prevents modifying other folders. Is there any workaround way?
The reason was The request was aborted: Could not create SSL/TLS secure channel., so I updated the code with this. Then it works fine.
Support Resource loading in a library, where you have no control over the calling executable.
I do not wish to load files from disk.
How can I fix the ConsoleApp2 code so it supports the described scenario?
Based on the Pack Uri documentation, I was able to do this:
What works
FontLibrary, a Class Library containing only font files with Build Action: Resource
ConsoleApp1, Console Application referencing this FontLibrary.
ConsoleApp1 code
Add reference to WindowsBase runtime assembly.
class Program
{
static void Main(string[] args)
{
// Workaround to register pack://application Uri format
var notUsedButNeeded = System.IO.Packaging.PackUriHelper.UriSchemePack;
var fontFamilies = System.Windows.Media.Fonts.GetFontFamilies(new Uri("pack://application:,,,/"), "/FontLibrary;Component/");
// This correctly lists the font families contained in the font library
Console.WriteLine("Number of font families: " + fontFamilies.Count);
}
}
What doesn't work:
Exception message is
The system cannot find the file specified. (Exception from HRESULT: 0x80070002)'
LogicLibrary, a Class Library referecing FontLibrary
FontLibrary, a Class Library containing only font files with Build Action: Resource
ConsoleApp2, a Console Application referencing LogicLibrary. This represents any consumer of the LogicLibrary, and I cannot add references from this library to the font library, only call LogicLibrary.
ConsoleApp2 code
class Program
{
static void Main(string[] args)
{
new FontLoader().Load();
}
}
LogicLibrary code
public class FontLoader
{
public void Load()
{
// Workaround to register pack://application Uri format
var notUsedButNeeded = System.IO.Packaging.PackUriHelper.UriSchemePack;
// Raises exception when called from ConsoleApp2, since the APPLICATION = the ConsoleApp2 does not reference the font library.
var fontFamilies = System.Windows.Media.Fonts.GetFontFamilies(new Uri("pack://application:,,,/"), "/FontLibrary;Component/");
Console.WriteLine("Number of font families: " + fontFamilies.Count);
}
}
Similar questions:
Using the correct pack:// URI Format
I have an issue finding the exception cause in FallBackFactory, mine is a old application, so i can not use spring cloud approach (with annotations etc..)
I found the below solution, but still not working for me:
Issue in getting cause in HystrixFeign client fallback
Here is the code i have:
public static class ProfileFallbackFactory implements ProfileProxy, FallbackFactory<ProfileFallbackFactory> {
final Throwable cause;
public ProfileFallbackFactory() {
this(null);
}
ProfileFallbackFactory(Throwable cause) {
this.cause = cause;
}
#Override
public ProfileFallbackFactory create(Throwable cause) {
LOG.info("Profile fallback create "+cause);
return new ProfileFallbackFactory(cause);
}
public Profile getProfile(String id) {
}
instance creation:
profileProxy = new HystrixFeign.Builder().setterFactory(new CustomSetterFactory())
.decode404()
.decoder(new GsonDecoder(gsonWithDateFormat))
.encoder(new GsonEncoder(gsonWithDateFormat))
.errorDecoder(new profileProxyErrorDecoder())
.target(ProfileProxy.class,profileServiceUrl, (FallbackFactory<ProfileFallbackFactory>)new ProfileFallbackFactory());
There is a logger added in ProfileProxyErrorDecoder class, but this logger is not found in logs. I can see com.netflix.hystrix.exception.HystrixRuntimeException in server logs
Can someone please point me where i am going wrong?
I need to redirect to a path under the current module:
modulePath = "/test";
Get["/there"] = ...
Get["/here"] = routeParams => { return ????????????("/there"); }
I expect "/test/here" to redirect to "/test/there"
You can use named routes and Nancy.Linker. See https://github.com/horsdal/Nancy.Linker
That is install Nancy.Linker from NuGet, take a dependency on IResourceLinker and change the code to:
public class YourModule
{
public YourModule(IResourceLinker linker)
{
Get["theRoute", "/there"] = ...
Get["/here"] = Response.AsRedirect(linker.BuildAbsoluteUri(this.Context, "theRoute");
}
{
You could use my BaseModule. It will let you organise your web app into loosely coupled units, with related elements in the same folder: modules, views, static resources. This is achieved by registering the module's namespace (shorn of project name) as 1) the modulePath, 2) a search location for views and 3) a permissible location for static content.
As such, you go (back) to having URLs that map to the folder structure of your project. Items you're likely to work on together, and delete together, live together. Constructing links to things is greatly simplified. So for your redirect, you would have a folder structure
here
|-module.cs
|-there
|-module.cs
and in your "here" module you just return Response.AsRedirect("there/");
For this to work, modules should inherit from the following BaseModule, and don't change the default namespace of your modules. It maps to the folder.
using Nancy;
using Nancy.Conventions;
using System;
using System.Text.RegularExpressions;
namespace HaciendaTestClient
{
// http://stackoverflow.com/questions/2287636/pass-current-object-type-into-base-constructor-call
public abstract class BaseModule<T> : NancyModule, IConvention
{
// the goal is to provide the folder path from the app root as the module path, to enable relative links to static content.
public BaseModule() : base(typeof(T).Namespace.Substring(typeof(T).Namespace.IndexOf('.')).Replace('.', '/'))
{
}
public void Initialise(NancyConventions conventions)
{
// This is where we register the top level directory containing the module as a root for static content.
var tlf = Regex.Match(typeof(T).Namespace, "(?<=\\.)[^\\.]*", RegexOptions.Singleline).Value;
var conv = StaticContentConventionBuilder.AddDirectory(tlf);
conventions.StaticContentsConventions.Add(conv);
}
public Tuple<bool, string> Validate(NancyConventions conventions)
{
return new Tuple<bool, string>(true, "no problem joe");
}
}
}
Here's a little screenshot of my solution explorer, to get you interested. Since everything is called with relative links, everything can be called "_". The location becomes the identifier. And it's not brittle. Folders can be moved, renamed and nested. Just pay attention that the namespaces correspond! Nancy rocks.
I would like to know if it's possible to access a file putted on the conf folder of tomcat.
Typically i will put configuration for multiple webapp , outside of the wars, in this file.
I want to use the classpath to be independent from the file system.
I used the lib folder in the past. It work great.
But it's a bit of a none sense using the lib folder to put conf file.
Can someone help me on this one?
I have seen lots of bad ways people do configuration in webapps that either makes it not really config (you have to do redeploy/release when you change config) or you have very little flexibility.
How I approach the problem is to use Spring for property placeholder but often times you need to bootstrap Spring or whatever you MVC stack before it loads with a property that says where to load config. I use a listener for that:
package com.evocatus.util;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class SimpleContextListenerConfig /*extend ResourceBundle */ implements ServletContextListener{
private ServletContext servletContext;
#Override
public void contextInitialized(ServletContextEvent sce) {
servletContext = sce.getServletContext();
servletContext.setAttribute(getClass().getCanonicalName(), this);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
public static String getProperty(ServletContext sc, String propName, String defaultValue) {
SimpleContextListenerConfig config = getConfig(sc);
return config.getProperty(propName, defaultValue);
}
public static SimpleContextListenerConfig getConfig(ServletContext sc) {
SimpleContextListenerConfig config =
(SimpleContextListenerConfig) sc.getAttribute(SimpleContextListenerConfig.class.getCanonicalName());
return config;
}
public String getProperty(String propName, String defaultValue)
{
/*
* TODO cache properties
*/
String property = null;
if (property == null)
property = servletContext.getInitParameter(propName);
if (property == null)
System.getProperty(propName, null);
//TODO Get From resource bundle
if (property == null)
property = defaultValue;
return property;
}
}
https://gist.github.com/1083089
The properties will be pulled first from the servlet context, then system properties thus allowing you to override for certain webapps.
You can change the config for certian webapp either by changing web.xml (not recommended) or by creating a context.xml
You can use the static methods to get the config:
public static SimpleContextListenerConfig getConfig(ServletContext sc);