egil / Htmxor

Supercharges Blazor static server side rendering (SSR) by seamlessly integrating the Htmx.org frontend library.
MIT License
118 stars 11 forks source link

HtmxContext request/response scoped type #2

Closed egil closed 4 months ago

egil commented 8 months ago

Provide a HtmxContext type that leverages the HttpContext to provide easy access to Htmx specific request headers and setting Htmx specific response headers and status codes.

The idea is to create a HtmxContext type that exposes HtmxRequest Request { get; } and HtmxResponse { get; } properties, which mirrors the HttpContext, HttpRequest and HttpReponse types users are already familiar with from asp.net core, i.e.:

public class HtmxContext(HttpContext context)
{
  public HtmxRequest Request { get; }
  public HtmxResponse Response { get; }
}

The implementation of HtmxRequest would just contain a bunch of properties that matches the names of the Htmx headers and make them available as the most relevant .NET types.

For HtmxResponse, besides having writable property setters, there may be additional methods that make sending Htmx headers correctly easier, if needed.


tanczosm commented 7 months ago

I fully understand your viewpoint on not integrating Khalid's htmx.net library. It does pigeon-hole you into a particular implementation, which can be a disadvantage of that approach and your point is definitely valid. I'm fine either way. You just have a bigger footprint of code to maintain.

I like the naming scheme on your approach, particularly with the boolean headers. The configuration options are much better. I think setting up the configuration on startup is sufficient.

Here's what I would address then:

egil commented 7 months ago

The configuration options are much better. I think setting up the configuration on startup is sufficient.

I agree. The sample app does this in the Program.cs and the <HtmxConfigHeadOutlet /> picks up the config from DI. The config is thus also available to any child component or service that needs it.

A question is whether the config object should be init only, and not have properties settable after the initial configuration. I am leaning towards init only.

builder.AddHtmx(config =>
{
    config.SelfRequestsOnly = true;
});

Make the htmx manipulation aspect of the library a separate package altogether though ...

I am not sure what you mean by "htmx manipulation aspect", but I do in general like the idea of having multiple sub-libraries with independent functionality that folks can depend on as they prefer. There would still be a Htmxor main package that includes everything, but it would just be a reference to all sub-packages (similar to have xunit is just a package that points to xunit.core, xunit.assert, etc.)

Header manipulation on the response end should always be done directly on the Response object. Trigger headers should not be overwritten but instead merged with existing Trigger headers. There is no guarantee that Htmxor will be the originator of existing Trigger headers.

In general, the HtmxResponse type should expose strongly typed properties that when set, directly update the HttpContext.Response property, and when read, read directly from HttpContext.Response. Specific headers like Trigger can have additional logic to merge previous set values, etc. as you suggest.

The ability to fluently chain response header manipulation calls together is pretty nice ( h.PushUrl('/new-url').WithTrigger("cool") )

Agree, that may work well. These kinds of methods could easily be added to HtmxResponse.

Header Keys should have an additional method that exposes an aggregate list of all htmx headers for CORS whitelisting

My CORS foo is not very strong. Can you explain this in more detail?


Do you want to take a stab at implementing the HtmxResponse type as discussed above and per your suggestions?

tanczosm commented 7 months ago

I'll take a stab at HtmxResponse.

tanczosm commented 7 months ago

I have something written now for HtmxResponse. I just need to run through tests and I'll put a PR up to take a look at it in the next day or so. The hardest part was trigger merging.

egil commented 4 months ago

Done. Thanks for the help with this @tanczosm .