KitchenPC.Imp 0.1.0

dotnet add package KitchenPC.Imp --version 0.1.0
                    
NuGet\Install-Package KitchenPC.Imp -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="KitchenPC.Imp" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KitchenPC.Imp" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="KitchenPC.Imp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add KitchenPC.Imp --version 0.1.0
                    
#r "nuget: KitchenPC.Imp, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package KitchenPC.Imp@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=KitchenPC.Imp&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=KitchenPC.Imp&version=0.1.0
                    
Install as a Cake Tool

Imp

Build

Imp (In Memory Pages) is a lightweight page framework built on ASP.NET Core middleware. It maps request paths to .NET classes, creates those classes through ASP.NET Core dependency injection, binds query-string values to page properties, and renders the response. Pages can render HTML directly or use HTML templates embedded in the application's assembly. Embedded templates are compiled once and cached in memory, so no page-template files need to be read from disk while the application is running.

Building and testing

Restore, build, and run the fast unit-test suite from the repository root:

dotnet test

The tests use in-memory request objects and do not start a web server or make network calls.

Create a local NuGet package and symbol package with:

dotnet pack Imp.csproj --configuration Release --output artifacts

Releasing

Every push and pull request builds, tests, and packs KitchenPC.Imp for validation. NuGet publication is intentionally separate and only runs for version tags. To publish a configured release, create and push a tag matching the package version:

git tag v0.1.0
git push origin v0.1.0

Package versions on NuGet are immutable. Never reuse a release tag or version; increment the version for each subsequent release.

Getting started

Reference the Imp project from an ASP.NET Core application:

<ItemGroup>
  <ProjectReference Include="..\Imp\Imp.csproj" />
</ItemGroup>

Register Imp near the end of the ASP.NET Core pipeline in Startup.cs:

using Imp;

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   app.UseStaticFiles();
   app.UseRouting();

   app.UseImp(config =>
      config.PageAssembly(typeof(Startup).Assembly)
         .RootPageNamespace("HelloWorld.Pages")
         .RootTemplateNamespace("HelloWorld.Templates")
         .NotFoundPageType<Pages.NotFound>()
   );
}

With the root page namespace set to HelloWorld.Pages, Imp uses the URL path to find a page class:

Request path Page type
/ HelloWorld.Pages.Default
/hello HelloWorld.Pages.Hello
/account/settings HelloWorld.Pages.Account.Settings

Page type lookup is case-insensitive. The root path is represented by a class named Default.

Hello World

Every page derives from BasePage. The smallest page can override Render and write directly to the ASP.NET Core response:

using System.Threading.Tasks;
using Imp;
using Microsoft.AspNetCore.Http;

namespace HelloWorld.Pages
{
   public sealed class Hello : BasePage
   {
      public override Task Render(HttpResponse response)
      {
         return response.WriteAsync("<h1>Hello, World!</h1>");
      }
   }
}

This page is available at /hello.

Query-string parameters

Imp binds query-string values to matching public writable properties before rendering the page. Property names are case-sensitive during binding.

using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Imp;
using Microsoft.AspNetCore.Http;

namespace HelloWorld.Pages
{
   public sealed class Hello : BasePage
   {
      public string Name { get; set; } = "World";

      public override Task Render(HttpResponse response)
      {
         string name = HtmlEncoder.Default.Encode(Name);
         return response.WriteAsync($"<h1>Hello, {name}!</h1>");
      }
   }
}

/hello?Name=Mike renders Hello, Mike!. Imp supports strings, GUIDs, dates, Boolean and numeric values, and enums. Nullable forms are supported for GUIDs, dates, Boolean values, and numeric values. Values should still be HTML-encoded before being placed in a response.

Embedded page templates

A page can keep its HTML in an embedded resource instead of implementing Render. First, include the template files as embedded resources in the website project:

<ItemGroup>
  <EmbeddedResource Include="PageTemplates\*.htm" />
  <EmbeddedResource Include="Templates\*.htm" />
</ItemGroup>

Create PageTemplates/Hello.htm:

<PageTemplate>
  <html>
    <head>
      <title>Imp example</title>
    </head>
    <body>
      <h1><Dynamic.Greeting /></h1>
    </body>
  </html>
</PageTemplate>

Associate the embedded resource with a page class and implement the dynamic element as a method:

using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Imp;
using Imp.TemplateManagers;

namespace HelloWorld.Pages
{
   [PageTemplate("HelloWorld.PageTemplates.Hello.htm")]
   public sealed class Hello : BasePage
   {
      public string Name { get; set; } = "World";

      public Task Greeting(TextWriter output, DynamicContentArgs args)
      {
         string name = HtmlEncoder.Default.Encode(Name);
         return output.WriteAsync($"Hello, {name}!");
      }
   }
}

<Dynamic.Greeting /> calls the page's Greeting method while the compiled template is being rendered. Dynamic methods accept a TextWriter and DynamicContentArgs and return a Task. Attributes on a dynamic element are available through args, for example <Dynamic.Link Text="Home" /> can read args["Text"].

Templates are XML-based and must be well-formed. The outer element for a page is <PageTemplate>.

Reusable templates

Reusable layout templates are embedded beneath the namespace configured by RootTemplateNamespace. A layout named Simple.htm starts with <Template> and can expose placeholders:

<Template>
  <html>
    <head><title><Placeholder.Title /></title></head>
    <body><Placeholder.Body /></body>
  </html>
</Template>

A page template supplies the placeholder content:

<PageTemplate>
  <Template.Simple>
    <Content.Title>Hello</Content.Title>
    <Content.Body>
      <p><Dynamic.Greeting /></p>
    </Content.Body>
  </Template.Simple>
</PageTemplate>

Page lifecycle and dependency injection

Imp creates page objects with ActivatorUtilities, so constructor dependencies registered with ASP.NET Core can be injected normally. For each HTML request, Imp:

  1. Resolves the page type from the request path.
  2. Invokes OnNotFound if no page type exists, then falls back to NotFoundPageType if necessary.
  3. Assigns the current HttpRequest to BasePage.Request and binds query-string properties.
  4. Runs Authenticate for pages marked with [SecurePage].
  5. Runs the global OnPreRender callback and the page's PreRender method.
  6. Calls IPostable.Postback for a POST request when the page implements IPostable.
  7. Renders the compiled template, or calls Render when the page has no template.

Configuration reference

All options use the fluent ImpConfiguration interface:

| Option | Purpose | | --- | --- | | PageAssembly(assembly) | Selects the assembly containing page classes and embedded template resources. | | RootPageNamespace(value) | Prefixes the namespace inferred from the request path. | | RootTemplateNamespace(value) | Sets the embedded-resource namespace used to locate reusable <Template> resources. | | NotFoundPageType<T>() | Selects the page rendered when neither normal routing nor OnNotFound resolves a request. | | OnNotFound(callback) | Runs custom fallback routing. The callback receives the HttpRequest and returns a page Type, or null to use the configured not-found page. Imp creates the returned type through dependency injection. This is useful for database-backed permalinks. | | CdnPrefix(value) | Prefixes attribute values written as cdn.href, cdn.src, and similar cdn.* attributes in templates. Imp removes cdn. from the rendered attribute name. | | OnBuildCdnPath(callback) | Allows the final CDN URL to be rewritten, for example to append a deployment version for cache invalidation. | | OnPreRender(callback) | Runs application code immediately before the page's own PreRender method. It receives the request and response. | | Authenticate(callback) | Authenticates pages marked with [SecurePage]. Returning false stops page rendering. | A more complete configuration might look like this:

private static readonly string CdnTag =
   $"?v={typeof(Startup).Assembly.GetName().Version}";

app.UseImp(config =>
   config.PageAssembly(typeof(Startup).Assembly)
      .RootPageNamespace("MySite.Pages")
      .RootTemplateNamespace("MySite.Templates")
      .NotFoundPageType<Pages.NotFound>()
      .CdnPrefix("https://cdn.example.com")
      .OnNotFound(request =>
         request.Path.StartsWithSegments("/articles")
            ? typeof(Pages.ArticlePermalink)
            : null
      )
      .OnBuildCdnPath(url => url + CdnTag)
      .OnPreRender((request, response) =>
         response.Headers["X-Rendered-By"] = "Imp"
      )
      .Authenticate((context, page) =>
      {
         if (context.User.Identity?.IsAuthenticated == true)
            return true;

         context.Response.Redirect("/login");
         return false;
      })
);

Not-found pages and custom routes

A basic not-found page can render directly:

public sealed class NotFound : BasePage
{
   public override Task Render(HttpResponse response)
   {
      response.StatusCode = StatusCodes.Status404NotFound;
      return response.WriteAsync("<h1>Page not found</h1>");
   }
}

OnNotFound is intended for URLs that cannot be represented by a fixed page class. A recipe site, for example, can recognize /recipes/chocolate-cake/ and return its permalink page type. Imp creates that type through ASP.NET Core dependency injection. If the callback returns null, Imp renders NotFoundPageType; if that type cannot be created, Imp uses its built-in not-found page.

CDN-aware template attributes

Prefix an HTML attribute with cdn. when its value should pass through Imp's CDN handling:

<link rel="stylesheet" cdn.href="/styles/site.css" />
<script cdn.src="/scripts/site.js"></script>

With CdnPrefix("https://cdn.example.com"), these render as normal href and src attributes pointing to the CDN. OnBuildCdnPath can then apply any final transformation, such as adding an application version to selected files. Without a CDN prefix, the paths remain local.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 110 8/19/2026