Kaliumhexacyanoferrat / GenHTTP

Lightweight web server written in pure C# with few dependencies to 3rd-party libraries.
https://genhttp.org
MIT License
192 stars 30 forks source link

`LayoutBuilder` is not functioning as intended. #375

Closed OoLunar closed 1 year ago

OoLunar commented 1 year ago

When passing a LayoutBuilder I'm expecting to be served the page. Instead I am receiving 404's and am unable to serve any content.

Example

LayoutBuilder textFiles = Layout.Create()
    .Add("/readme", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "Readme.md")))
    .Add("/license", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "License")))
    .Add("/privacy", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "PrivacyPolicy.md")))
    .Add("/terms", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "TermsOfService.md")))
    .Add("/favicon.ico", Download.From(Resource.FromAssembly(typeof(Program).Assembly, "res.icon.ico")));

Host.Create()
    .Handler(textFiles)
    .Bind(IPAddress.Parse(configuration.GetValue("Server:Address", "127.0.0.1")!), configuration.GetValue<ushort>("Server:Port", 8080))
    .Run();

Additional Information Running on the latest commit, using https://genhttp.org/documentation/content/layouting as a reference.

Kaliumhexacyanoferrat commented 1 year ago

The issue is due to the leading slashes (/). If you change to .Add("readme" ...) the pages are served.

Besides that, it seems that you would like to serve a website, so I would take this approach instead:

var textFiles = Layout.Create()
                      .Add("readme", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "Readme.md")).Title("Readme"));

var website = Website.Create()
                     .Theme(Theme.Create())
                     .Content(textFiles);
                     //.Favicon(Resource.FromAssembly(...))

Host.Create()
    .Handler(website)
    .Bind(IPAddress.Parse("127.0.0.1"), 8080)
    .Run();

With the NoTheme package, if the pages should be served without a menu. Otherwise you can try something like AdminLTE,

Kaliumhexacyanoferrat commented 1 year ago

Sample project: sample.zip

OoLunar commented 1 year ago

Thank you for your response and I apologize for the delay.

As you had mentioned, the issue was indeed the leading slash. I hadn't seen this documented anywhere and I expected the leading slash to be treated as the root of the website, not as a segment.

With the NoTheme package, if the pages should be served without a menu. Otherwise you can try something like AdminLTE,

While I appreciate the offer, the latest Nuget packages don't seem to work as intended with the latest commit of this project. Instead, it throws upon registering due to a lack of implementation on the Build method, I think? Regardless, that's out of scope for this issue.

Thank you again, I appreciate your assistance.

This was my final result:

```cs serviceCollection.AddSingleton((serviceProvider) => { DiscordHeaderAuthentication discordHeaderAuthentication = serviceProvider.GetRequiredService(); IConfiguration configuration = serviceProvider.GetRequiredService(); InteractionHandler interactionHandler = serviceProvider.GetRequiredService(); JsonErrorMapper jsonErrorMapper = serviceProvider.GetRequiredService(); HttpLogger httpLogger = serviceProvider.GetRequiredService(); MarkdownPageProviderBuilder readme = ModMarkdown .Page(Resource.FromAssembly(typeof(Program).Assembly, "Readme.md")) .Title("Readme - Cookie Clicker") .Description("Are you tired of having no purpose in life? Do you crave the sweet satisfaction of watching a number increase every time you click? Look no further, because the cookie clicker bot is here to fill that void in your soul."); LayoutBuilder textFiles = Layout.Create() .Add("readme", readme) .Add("license", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "License")) .Title("License - Cookie Clicker") .Description("The LGPL 3 is a permissive open-source license that allows the use and modification of software, while requiring any changes made to the original code to be released under the same LGPL 3 license. The license also allows for the linking of the licensed code with proprietary software under certain conditions.")) .Add("privacy", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "PrivacyPolicy.md")) .Title("Privacy - Cookie Clicker") .Description("Privacy Policy for the Cookie Clicker Discord bot: Your data is only collected for the purpose of providing services and is securely stored. We won't share your data with third parties, except as required by law or legal process, or to protect the rights, property, or safety of the Bot, its users, or others.")) .Add("terms", ModMarkdown.Page(Resource.FromAssembly(typeof(Program).Assembly, "TermsOfService.md")) .Title("Terms of Service - Cookie Clicker") .Description("Cookie Clicker Discord bot's Terms of Service outline prohibited conduct, user responsibility, disclaimer of warranty and liability, indemnification, termination, and changes to the terms and conditions. By using the bot, you agree to these terms.")) .Add("favicon.ico", Download.From(Resource.FromAssembly(typeof(Program).Assembly, "favicon.ico"))) .Add("res", Listing.From(ResourceTree.FromAssembly(typeof(Program).Assembly, "res"))) .Index(readme); InlineBuilder inlineBuilder = Inline.Create() .Authentication(ApiKeyAuthentication.Create() .WithHeader("Host") .Authenticator(discordHeaderAuthentication.Authenticate)) .Post(interactionHandler.Handle); LayoutBuilder root = Layout.Create() .Add(textFiles) .Add("api", inlineBuilder); string? basePath = configuration.GetValue("Server:BasePath")?.TrimStart('/'); return Host.Create() .Defaults() .Companion(httpLogger) .Handler((string.IsNullOrWhiteSpace(basePath) ? Layout.Create().Add(root) : Layout.Create().Add(basePath, root) ).Add(ErrorHandler.From(jsonErrorMapper))) .Bind(IPAddress.Parse(configuration.GetValue("Server:Address", "127.0.0.1")!), configuration.GetValue("Server:Port", 8080)) .RequestReadTimeout(TimeSpan.FromSeconds(configuration.GetValue("Server:RequestReadTimeout", 30))) .RequestMemoryLimit(configuration.GetValue("Server:RequestMemoryLimit", 1024 * 1024 * 10)); }); ```