### Start Development Server with npm Source: https://github.com/lanayx/oxpecker/blob/develop/examples/EmptySolid/README.md Run this command to start the development server. This is typically used during the development phase to see changes live. ```bash npm start ``` -------------------------------- ### Giraffe Application Setup Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Demonstrates the service and application builder configuration for a Giraffe web application. ```fsharp // Giraffe let configureApp (appBuilder: IApplicationBuilder) = appBuilder .UseGiraffe(webApp) let configureServices (services: IServiceCollection) = services .AddGiraffe() |> ignore ``` -------------------------------- ### Install Dependencies with npm Source: https://github.com/lanayx/oxpecker/blob/develop/examples/EmptySolid/README.md Use this command to install project dependencies using npm. Ensure Node.js is installed. ```bash npm install ``` -------------------------------- ### Oxpecker Application Setup Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Shows the equivalent service and application builder configuration for an Oxpecker web application, including `UseRouting` and `AddRouting`. ```fsharp // Oxpecker let configureApp (appBuilder: IApplicationBuilder) = appBuilder .UseRouting() .UseOxpecker(endpoints) let configureServices (services: IServiceCollection) = services .AddRouting() .AddOxpecker() |> ignore ``` -------------------------------- ### hx-download: Save Response as File Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Htmx/README.md Initiates a file download by making a GET request and specifying the download swap method. ```fsharp button().hxGet("/files/report.pdf").hxSwap(HxSwapMethod.download) { "Download" } ``` -------------------------------- ### Install Oxpecker NuGet Package Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use the Package Manager Console to install the Oxpecker NuGet package. ```powershell PM> Install-Package Oxpecker ``` -------------------------------- ### Custom Model Binder Configuration Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Example of configuring custom model binders during application startup. This allows for custom logic or options for model binding. ```fsharp let configureServices (services : IServiceCollection) = // First register all default Oxpecker dependencies services.AddOxpecker() |> ignore // Now register custom model binder services.AddSingleton(CustomModelBinder()) |> ignore // or use default model binder, but with different options services.AddSingleton(ModelBinder(specificOptions)) |> ignore ``` -------------------------------- ### F# Markup Example with Oxpecker.ViewEngine Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Demonstrates creating an HTML structure using Oxpecker.ViewEngine's computation expressions, including nested elements, attributes, and dynamic content. ```fsharp open Oxpecker.ViewEngine type Person = { Name: string } let subView = p() { "Have a nice day" } let mainView (model: Person) = html() { body(style="width: 800px; margin: 0 auto") { h1(style="text-align: center; color: red") { $"Hello, {model.Name}!" } subView ul() { for i in 1..10 do br() li().attr("onclick", $"alert('Test {i}')") { span(id= $"span{i}", class'="test") { i } } } } } ``` -------------------------------- ### Implement gRPC Service in F# Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Implement a gRPC service in F# by inheriting from the generated base class. This example shows a simple Greeter service. ```fsharp type GreeterService() = inherit Greeter.GreeterBase() override _.SayHello (request, _) = HelloReply(Message = $"Hello {request.Name}") |> Task.FromResult ``` -------------------------------- ### Route Parameter Syntax Change Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Illustrates the change in route parameter syntax from Giraffe's `%s/%O` to Oxpecker's `/{%s}/{%O:guid}`. Note the requirement for a GUID constraint in Oxpecker for `%O`. ```fsharp // Giraffe routef "/hello/%s/%O" (fun (a, b) -> doSomething a b) // Oxpecker routef "/hello/{%s}/{%O:guid}" (fun a b -> doSomething a b) ``` -------------------------------- ### hx-sse: Persistent Stream Example Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Htmx/README.md Connects to a Server-Sent Events stream and appends messages to a log, using 'beforeend' swap method. ```fsharp div().hxSseConnect("/log").hxSwap("beforeend") { h3() { "Log:" } } ``` -------------------------------- ### Filtering Routes by HTTP Verb (GET, POST) Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Define routes that respond differently based on the HTTP verb. Use GET and POST blocks to group routes for specific methods. ```fsharp let submitFooHandler : EndpointHandler = // Do something let submitBarHandler : EndpointHandler = // Do something let webApp = [ // Filters for GET requests GET [ route "/foo" <| text "Foo" route "/bar" <| text "Bar" ] // Filters for POST requests POST [ route "/foo" <| submitFooHandler route "/bar" <| submitBarHandler ] ] ``` -------------------------------- ### Create EndpointMiddleware with Exception Handling Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md EndpointMiddleware is constructed similarly to EndpointHandler but accepts an additional EndpointHandler as the first parameter. This example demonstrates a try-catch middleware. ```fsharp let tryCatchMW : EndpointMiddleware = fun (next: EndpointHandler) (ctx: HttpContext) -> task { try return! next ctx with | ex -> ctx.Response.StatusCode <- 500 return! text (sprintf "An error occurred: %s" ex.Message) ctx } ``` -------------------------------- ### Integrate Custom HTML View Engine Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Extend `HttpContext` to plug in a custom view engine. This example shows how to convert a custom `MyHtmlElement` to bytes and write them to the response. ```fsharp [] static member WriteMyHtmlView(ctx: HttpContext, htmlView: MyHtmlElement) = let bytes = htmlView |> convertToBytes ctx.Response.ContentType <- "text/html; charset=utf-8" ctx.WriteBytes bytes // ... let myHtmlView (htmlView: MyHtmlElement) : EndpointHandler = fun (ctx: HttpContext) -> ctx.WriteMyHtmlView htmlView ``` -------------------------------- ### hx-targets: Swap Response into Multiple Elements Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Htmx/README.md Fetches data using a GET request and swaps the response into all elements matching the '.alert-box' selector. ```fsharp button().hxGet("/api/notification").hxTargets(".alert-box") { "Refresh All" } ``` -------------------------------- ### Configure and Run gRPC Service in ASP.NET Core Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Configure services to add gRPC support and map the gRPC service endpoint in your ASP.NET Core application. This setup is required for gRPC communication. ```fsharp let configureServices (services: IServiceCollection) = services // other dependencies .AddGrpc() // Register GRPC dependencies |> ignore [] let main args = let builder = WebApplication.CreateBuilder(args) configureServices builder.Services let app = builder.Build() app.MapGrpcService() |> ignore // expose Grpc endpoint app.Run() 0 ``` -------------------------------- ### ASP.NET Core 9+ OpenAPI Configuration Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.OpenApi/README.md Configures an ASP.NET Core application with Oxpecker and OpenAPI support for .NET 9+. Includes service configuration and application pipeline setup, enabling the JSON OpenAPI endpoint. ```fsharp let configureApp (appBuilder: IApplicationBuilder) = appBuilder .UseRouting() .Use(errorHandler) .UseOxpecker(endpoints) .Run(notFoundHandler) let configureServices (services: IServiceCollection) = services .AddRouting() .AddOxpecker() .AddOpenApi( // support for Option<_> and ValueOption<_> (ASP.NET Core 10+) fun o -> o.AddSchemaTransformer() |> ignore ) // OpenAPI dependencies |> ignore [] let main args = let builder = WebApplication.CreateBuilder(args) configureServices builder.Services let app = builder.Build() configureApp app app.MapOpenApi() |> ignore // for json OpenAPI endpoint app.Run() 0 ``` -------------------------------- ### ASP.NET Core 8+ OpenAPI Configuration with Swashbuckle Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.OpenApi/README.md Configures an ASP.NET Core application with Oxpecker, Swashbuckle, and OpenAPI support for .NET 8+. Includes service configuration and application pipeline setup, enabling Swagger UI and JSON OpenAPI endpoint. ```fsharp let configureApp (appBuilder: IApplicationBuilder) = appBuilder .UseRouting() .Use(errorHandler) .UseOxpecker(endpoints) .UseSwagger() // for json OpenAPI endpoint .UseSwaggerUI() // for viewing Swagger UI .Run(notFoundHandler) let configureServices (services: IServiceCollection) = services .AddRouting() .AddOxpecker() .AddEndpointsApiExplorer() // use the API Explorer to discover and describe endpoints .AddSwaggerGen() // swagger dependencies |> ignore [] let main args = let builder = WebApplication.CreateBuilder(args) configureServices builder.Services let app = builder.Build() configureApp app app.Run() 0 ``` -------------------------------- ### Enable ARIA Attributes Support Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Open the Aria module to enable ARIA attributes support for elements. This example shows how to apply ARIA attributes to a span element. ```fsharp open Oxpecker.ViewEngine.Aria let x = span( role="checkbox", id="checkBoxInput", ariaChecked="false", tabindex=0, ariaLabelledBy="chk15-label" ) ``` -------------------------------- ### Build Frontend Solution Source: https://github.com/lanayx/oxpecker/blob/develop/AGENTS.md Build the primary frontend solution using dotnet build. ```bash dotnet build Oxpecker.Solid.slnx ``` -------------------------------- ### Build Backend Solution Source: https://github.com/lanayx/oxpecker/blob/develop/AGENTS.md Build the primary backend solution using dotnet build. ```bash dotnet build Oxpecker.slnx ``` -------------------------------- ### Build Application for Production with npm Source: https://github.com/lanayx/oxpecker/blob/develop/examples/EmptySolid/README.md Execute this command to build the application for production. This command compiles and optimizes the project for deployment. ```bash npm run build ``` -------------------------------- ### Get Query String Value Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Safely retrieves a query string parameter value. Returns 'default value' if the parameter is not found. ```fsharp let someHandler : EndpointHandler = fun (ctx: HttpContext) -> let someValue = match ctx.TryGetQueryValue "q" with | None -> "default value" | Some q -> q // Do something with `someValue`... // Return a Task ``` -------------------------------- ### Configure Router Component in F# Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Solid/README.md Set up a router component using `Oxpecker.Solid.Router`. Each route needs to be defined with a `path` and a `component`. Remember to add `@solidjs/router` to your `package.json`. ```fsharp open Oxpecker.Solid.Router [] let MyRouter () = Router() { Route(path="/", component'=Home) Route(path="/about", component'=About) } render (MyRouter, document.getElementById "root") ``` -------------------------------- ### Configure Endpoints with `configureEndpoint` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Illustrates configuring an endpoint using ASP.NET Core's `.With*` extension methods via the `configureEndpoint` function. ```fsharp let webApp = GET [ route "/foo" (text "Foo") |> configureEndpoint _.WithMetadata("foo") .WithDisplayName("Foo") ] ``` -------------------------------- ### Define Catch-all Routes with Asterisk Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Shows how to define catch-all routes using asterisk (*) or double asterisk (**) in conjunction with `route` and `routef`, leveraging ASP.NET Core's templating system. ```fsharp let webApp = GET [ route "/foo/{*bar}" (fun ctx -> text (ctx.TryGetRouteValue("bar") |> Option.defaultValue "") ctx) routef "/moo/{**%s}" (fun bar -> text bar) ] ``` -------------------------------- ### Configuring Endpoints with `configureEndpoint` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Shows how to use `configureEndpoint` to apply ASP.NET Core's `.With*` extension methods for endpoint customization. ```APIDOC ### configureEndpoint This function allows you to configure an endpoint using ASP.NET `.With*` extension methods: ```fsharp let webApp = GET [ route "/foo" (text "Foo") |> configureEndpoint _.WithMetadata("foo") .WithDisplayName("Foo") ] ``` ``` -------------------------------- ### hxStatus Modifier: Wildcard Status Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Htmx/README.md Configures a GET request to perform no swap for any 5xx status code, rendering 'hx-status:5xx="swap:none"'. ```fsharp div().hxGet("/data").hxStatus("5xx", "swap:none") { ... } ``` -------------------------------- ### Basic Routing with `route` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Illustrates the simplest form of routing using the `route` http handler to map URLs to responses. ```APIDOC ## Routing ### route The simplest form of routing can be done with the `route` http handler: ```fsharp let webApp = [ route "/foo" <| text "Foo" route "/bar" <| text "Bar" ] ``` ``` -------------------------------- ### Run Frontend Unit Tests Source: https://github.com/lanayx/oxpecker/blob/develop/AGENTS.md Execute frontend unit tests using dotnet test. Ensure xUnit and FsUnit.Light assertions are used. ```bash dotnet test tests/Oxpecker.Solid.Tests/Oxpecker.Solid.Tests.fsproj ``` -------------------------------- ### Run ViewEngine Unit Tests Source: https://github.com/lanayx/oxpecker/blob/develop/AGENTS.md Execute ViewEngine unit tests using dotnet test. Ensure xUnit and FsUnit.Light assertions are used. ```bash dotnet test tests/Oxpecker.ViewEngine.Tests/Oxpecker.ViewEngine.Tests.fsproj ``` -------------------------------- ### Run Backend Unit Tests Source: https://github.com/lanayx/oxpecker/blob/develop/AGENTS.md Execute backend unit tests using dotnet test. Ensure xUnit and FsUnit.Light assertions are used. ```bash dotnet test tests/Oxpecker.Tests/Oxpecker.Tests.fsproj ``` -------------------------------- ### Authentication Handler: Return Early with 401 in Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md This EndpointHandler checks user authentication. If not authenticated, it sets a 401 status code and explicitly starts the response with an 'Unauthorized' text, preventing further pipeline execution. ```fsharp let checkUserIsLoggedIn : EndpointHandler = fun (ctx: HttpContext) -> if isNotNull ctx.User && ctx.User.Identity.IsAuthenticated then Task.CompletedTask else ctx.SetStatusCode 401 text "Unauthorized" ctx // start response ``` -------------------------------- ### Create Custom YAML HTTP Handler Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates creating a custom `yaml` endpoint handler by serializing an object to YAML and writing it as bytes. This approach is useful for unsupported media types. ```fsharp let yaml (x: obj) : EndpointHandler = setHttpHeader "Content-Type" "text/yaml" >=> bytes (x |> YamlSerializer.toYaml |> Encoding.UTF8.GetBytes) ``` -------------------------------- ### Validate HTTP Preconditions with HttpContext Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use `HttpContext.ValidatePreconditions` to handle conditional HTTP requests based on ETag and Last-Modified headers. It returns an enum indicating the condition status, guiding the server's response. ```fsharp let someHttpHandler eTag lastModified : EndpointHandler = fun (ctx: HttpContext) -> task { match ctx.ValidatePreconditions(eTag, lastModified) with | ConditionFailed -> return ctx.PreconditionFailedResponse() | ResourceNotModified -> return ctx.NotModifiedResponse() | AllConditionsMet | NoConditionsSpecified -> // Continue as normal // Do stuff } ``` ```fsharp let webApp = [ route "/" <| text "Hello World" route "/foo" <| someHttpHandler None None ] ``` -------------------------------- ### Catch-all Routes Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Explains how to define catch-all routes using asterisk (*) or double asterisk (**) with `route` and `routef` to capture arbitrary path segments. ```APIDOC ### Catch-all route You can leverage the [templating system](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-8.0#route-templates) of ASP.NET core routing with Oxpecker to define catch-all routes with both `route` and `routef` functions by using asterisk * or double asterisk **: ```fsharp let webApp = GET [ route "/foo/{*bar}" (fun ctx -> text (ctx.TryGetRouteValue("bar") |> Option.defaultValue "") ctx) routef "/moo/{**%s}" (fun bar -> text bar) ] ``` ``` -------------------------------- ### Handle More Than 5 Route Parameters with `route` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates how to handle routes with more than the `routef` limit of 5 parameters by using the `route` handler and `TryGetRouteValue`. ```fsharp route "/{a}/{b}/{c}/{d}/{e}/{f}" (fun ctx -> let a = ctx.TryGetRouteValue("a") |> Option.defaultValue "" let b = ctx.TryGetRouteValue("b") |> Option.defaultValue "" let c = ctx.TryGetRouteValue("c") |> Option.defaultValue "" let d = ctx.TryGetRouteValue("d") |> Option.defaultValue "" let e = ctx.TryGetRouteValue("e") |> Option.defaultValue "" let f = ctx.TryGetRouteValue("f") |> Option.defaultValue "" text (sprintf "%s %s %s %s %s %s", a b c d e f) ctx ) ``` -------------------------------- ### F# Solid Component with Reactive State and Event Handling Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Solid/README.md Demonstrates creating a Solid component using F# with the `SolidComponent` attribute. It includes usage of reactive state with `createSignal`, rendering lists with `For`, and handling click events on a button. The `index()` getter from `For` is reactive. ```fsharp open Oxpecker.Solid // this library namespace open Browser // Fable browser bindings (needed for console.log) [] // this attribute is required to compile components to JSX that Solid understands let itemList items = // regular function arguments, no props! let x, setX = createSignal 0 // just sample of reactive state usage ul() { // ul tag For(each = items) { // Solid's built-in For component yield fun item index -> // function called for each item li() { // li tag span() { index() } // index is reactive getter from Solid span() { item.Name } // string field inside span tag button(onClick=fun _ -> console.log("Removing item...")) { // onClick event handler "Remove" // button text } } } } ``` -------------------------------- ### Creating a Custom HTML Tag Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Shows how to create a custom HTML tag by inheriting from RegularNode. ```fsharp type myTag() = inherit RegularNode("myTag") // will render ``` -------------------------------- ### Parameterized Routing with `routef` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Shows how to use `routef` for routes with user-defined parameters, supporting format strings and route constraints. ```APIDOC ### routef If a route contains user defined parameters then the `routef` http handler can be handy: ```fsharp let fooHandler first last age : EndpointHandler = fun (ctx: HttpContext) -> (sprintf "First: %s, Last: %s, Age: %i" first last age |> text) ctx let webApp = [ routef "/foo/{%s:maxlength(8)}/{%s}/{%i}" fooHandler routef "/bar/{%O:guid}" (fun (guid: Guid) -> text (string guid)) ] ``` The `routef` http handler takes two parameters - a format string and an `EndpointHandler` function. The built-in ASP.NET [route constraints](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#route-constraints) are fully supported as well. The format string supports the following format chars: | Format Char | Type | |-------------|------------------| | `%b` | `bool` | | `%c` | `char` | | `%s` | `string` | | `%i` | `int` | | `%d` | `int64` | | `%f` | `float`/`double` | | `%u` | `uint64` | | `%O:guid` | `Guid` | _Note_: `routef` handler can only handle up to 5 route parameters. It's not recommended to use more than 3 parameters in a route, but if you really need a lot, you can use `route` with `EndpointHandler` function that utilizes `.TryGetRouteValue` extension. ```fsharp route "/{a}/{b}/{c}/{d}/{e}/{f}" (fun ctx -> let a = ctx.TryGetRouteValue("a") |> Option.defaultValue "" let b = ctx.TryGetRouteValue("b") |> Option.defaultValue "" let c = ctx.TryGetRouteValue("c") |> Option.defaultValue "" let d = ctx.TryGetRouteValue("d") |> Option.defaultValue "" let e = ctx.TryGetRouteValue("e") |> Option.defaultValue "" let f = ctx.TryGetRouteValue("f") |> Option.defaultValue "" text (sprintf "%s %s %s %s %s %s", a b c d e f) ctx ) ``` ``` -------------------------------- ### Implement Meta Provider for Head Elements in F# Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Solid/README.md Use `Oxpecker.Solid.Meta` to manage elements in the application's `` section. Wrap your head elements within `MetaProivder`. You need to add `@solidjs/meta` to your `package.json`. ```fsharp open Oxpecker.Solid.Meta [] let Root () = MetaProivder() { Title() { "My App" } } ``` -------------------------------- ### Configure Custom JSON Serializer Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Register a custom `IJsonSerializer` implementation or configure the default `System.Text.Json` serializer with custom options during application startup. ```fsharp let configureServices (services : IServiceCollection) = // First register all default Oxpecker dependencies services.AddOxpecker() |> ignore // Now register custom serializer services.AddSingleton(CustomSerializer()) |> ignore // or use default STJ serializer, but with different options services.AddSingleton( SystemTextJsonSerializer(specificOptions)) |> ignore ``` -------------------------------- ### Add OpenAPI Metadata to Endpoints Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.OpenApi/README.md Demonstrates adding OpenAPI metadata to endpoints using `addOpenApi` for detailed configuration and `addOpenApiSimple` for simpler cases. Note that HTTP methods must be specified for OpenAPI support. ```fsharp open Oxpecker open Oxpecker.OpenApi open System.Threading.Tasks let endpoints = [ // addOpenApi supports passing detailed configuration POST [ route "/product" (text "Product posted!") |> addOpenApi (OpenApiConfig( requestBody = RequestBody(typeof), responseBodies = [| ResponseBody(typeof) |], configureOperation = (fun o _ _ -> o.OperationId <- "PostProduct"; Task.CompletedTask) )) ] // addOpenApiSimple is a shortcut for simple cases GET [ routef "/product/{%i}" ( fun id -> products |> Array.find (fun f -> f.Id = num) |> json ) |> configureEndpoint _.WithName("GetProduct") |> addOpenApiSimple ] // such route won't work with OpenAPI, since HTTP method is not specified route "/hello" <| text "Hello, world!" ] ``` -------------------------------- ### HTTP Handler Implementation: Giraffe vs. Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Illustrates the difference in implementing HTTP handlers between Giraffe and Oxpecker. Oxpecker's `EndpointHandler` is simpler as it omits the 'next' parameter. ```fsharp let someHandler (str: string) : HttpHandler = fun (next: HttpFunc) (ctx: HttpContext) -> task { return! ctx.WriteTextAsync str } ``` ```fsharp let someHandler (str: string) : EndpointHandler = fun (ctx: HttpContext) -> task { return! ctx.WriteText str } ``` -------------------------------- ### Define Simple Routes with `route` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Uses the `route` http handler to define simple, case-insensitive routes for specific paths. ```fsharp let webApp = [ route "/foo" <| text "Foo" route "/bar" <| text "Bar" ] ``` -------------------------------- ### Categorize Routes with `subRoute` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Shows how to use `subRoute` to group related routes under a common prefix, simplifying URL structure. ```fsharp let webApp = subRoute "/api" [ subRoute "/v1" [ route "/foo" <| text "Foo 1" route "/bar" <| text "Bar 1" ] subRoute "/v2" [ route "/foo" <| text "Foo 2" route "/bar" <| text "Bar 2" ] ] ``` -------------------------------- ### Compose Bind Query with Handler Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates composing a bindQuery helper with a handler for a specific route. This pattern is useful for extracting query parameters before invoking the main handler. ```fsharp route "/test" (bindQuery handler) ``` -------------------------------- ### Oxpecker.Solid Components: Regular Functions Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Solid/README.md Illustrates two ways to define components in Oxpecker.Solid using regular F# functions. `Component1` takes an accessor for reactive data, while `Component2` accepts a string and children. The `Test` component shows how to use these components, including managing reactive state with `createSignal`. ```fsharp // without children [] let Component1 (getText: Accessor) = h1(onClick = fun _ -> console.log(getText())) { getText() } // with children [] let Component2 (hello: string) (children: #HtmlElement) = h1() { hello children } // usage [] let Test () = let getText, _ = createSignal "Hello," div() { Component1 getText Component2 "world" <| span() { "!" } } ``` -------------------------------- ### Access Configuration in Service Configuration Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Access the IConfiguration object when configuring services by building a temporary service provider. ```fsharp let configureServices (services: IServiceCollection) = let serviceProvider = services.BuildServiceProvider() let settings = serviceProvider.GetService() // Configure services using the `settings`... services.AddOxpecker() |> ignore ``` -------------------------------- ### Creating a Dropdown with Alpine.js Directives Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Alpine/README.md This snippet demonstrates how to create a dropdown component using Oxpecker.Alpine's typed directives for x-data, x-on, and x-show. Ensure the Oxpecker.Alpine namespace is opened. ```fsharp open Oxpecker.ViewEngine open Oxpecker.Alpine let dropdown = div().xData("{ open: false }") { button(type' = "button").xOn("click", "open = !open") { "Toggle" } div().xShow("open").xOn("click.outside", "open = false") { "Dropdown contents" } } ``` -------------------------------- ### Deferred Task Execution - Resource Disposal Before Response Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates how a Task<'T> is a promise and might not complete before OxpeckerMiddleware returns. In this case, an IDisposable is disposed before the response is sent. ```fsharp let doSomething : EndpointHandler = fun ctx -> use __ = somethingToBeDisposedAtTheEndOfTheRequest text "Hello" ctx ``` -------------------------------- ### Adding Metadata with `addMetadata` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates how to attach metadata to a route using `addMetadata`, which can be utilized later in the request pipeline. ```APIDOC ### addMetadata It lets you add [metadata](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing#endpoint-metadata) to a route which can be used later on in the pipeline: ```fsharp let webApp = GET [ route "/foo" (text "Foo") |> addMetadata "foo" ] ``` ``` -------------------------------- ### Create EndpointHandler with Async Operations Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md The most verbose method involves explicitly returning a Task, useful for calling asynchronous operations within an EndpointHandler. ```fsharp type Person = { Name : string } let sayHelloWorld : EndpointHandler = fun (ctx: HttpContext) -> task { let! person = ctx.BindJson() let greeting = sprintf "Hello World, from %s" person.Name return! text greeting ctx } ``` -------------------------------- ### HTTP Status Code Handling: Giraffe vs. Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Demonstrates the change in handling HTTP status codes. Giraffe uses a module like `Successful.OK`, while Oxpecker integrates with `IResult` and uses `ctx.Write` with `TypedResults`. ```fsharp // Giraffe Successful.OK myObject next ctx ``` ```fsharp // Oxpecker ctx.Write <| TypedResults.Ok myObject ``` -------------------------------- ### Access Configuration Options in EndpointHandler Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Retrieve configuration options, such as IOptions, within an EndpointHandler using the GetService extension method. ```fsharp let someHandler : EndpointHandler = fun (ctx: HttpContext) -> let settings = ctx.GetService>() // Do something with `settings`... // Return a Task ``` -------------------------------- ### Access Hosting Environment in EndpointHandler Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Easily retrieve the IWebHostEnvironment object within an EndpointHandler using the GetHostingEnvironment extension method. ```fsharp let someHandler : EndpointHandler = fun (ctx: HttpContext) -> let env = ctx.GetHostingEnvironment() // Do something with `env`... // Return a Task ``` -------------------------------- ### Applying Custom HTTP Verb Combinations Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Create custom endpoint filters for specific combinations of HTTP verbs using applyHttpVerbsToEndpoints. Useful for advanced routing scenarios. ```fsharp let GET_HEAD_OPTIONS: Endpoint seq -> Endpoint = applyHttpVerbsToEndpoints(Verbs [ HttpVerb.GET; HttpVerb.HEAD; HttpVerb.OPTIONS ]) let webApp = [ GET_HEAD_OPTIONS [ route "/foo" <| text "Foo" route "/bar" <| text "Bar" ] ] ``` -------------------------------- ### Routing Configuration Comparison Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Compares the routing configuration structure between Giraffe and Oxpecker. Oxpecker uses a list-based approach for defining routes, while Giraffe uses a `choose` function. ```fsharp // Giraffe let webApp = (choose [ GET_HEAD >=> routef "/hello/%s" (fun name -> text $"Hello {name}!") GET >=> (choose [ route "/foo" >=> setHttpHeader "X-Version" "1" >=> text "Bar" subRoute "/v2" >=> (choose [ route "/foo" >=> text "Bar2" ]) ]) ]) // Oxpecker let webApp = [ GET_HEAD [ routef "/hello/{%s}" (fun name -> text $"Hello {name}!") ] GET [ route "/foo" (setHttpHeader "X-Version" "1" >=> text "Bar") subRoute "/v2" [ route "/foo" <| text "Bar2" ] ] ] ``` -------------------------------- ### Configuring Default Error Handling Middleware Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Set up default exception handling and not found middleware in the ASP.NET Core application pipeline. Ensure exception middleware is placed before Oxpecker. ```fsharp let configureApp (appBuilder: IApplicationBuilder) = appBuilder .UseRouting() .Use(Default.exceptionMiddleware) // Add exception handling middleware BEFORE Oxpecker .UseOxpecker(endpoints) .Run(Default.notFoundHandler) // Add not found middleware AFTER Oxpecker ``` -------------------------------- ### Add OpenAPI Simple Method Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.OpenApi/README.md Use this method for simple OpenAPI cases where request and response schemas can be inferred from generic types. Pass `unit` for request or response types if they are not applicable. ```fsharp let addOpenApiSimple<'Req, 'Res> = ... ``` -------------------------------- ### Create EndpointHandler by Re-using Existing Function Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md The simplest way to create an EndpointHandler is by directly assigning an existing EndpointHandler function. ```fsharp let sayHelloWorld : EndpointHandler = text "Hello World, from Oxpecker" ``` -------------------------------- ### Open Oxpecker.Htmx Namespaces Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Htmx/README.md Opens the necessary namespaces for using Oxpecker.Htmx and its extensions. ```fsharp open Oxpecker.Htmx open Oxpecker.Htmx.Extensions ``` -------------------------------- ### Configure ASP.NET Core App with Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Integrate Oxpecker into the ASP.NET Core pipeline after UseRouting. Supports various route definitions. ```fsharp open Oxpecker // usually your application consists of several routes let webApp = [ route "/" <| text "Hello world" route "/ping" <| text "pong" ] // sometimes it can only be a single route let webApp1 = route "/" <| "Hello Oxpecker" // or it can only be a single "MultiEndpoint" route let webApp2 = GET [ route "/" <| "Hello Oxpecker" ] let configureApp (appBuilder: IApplicationBuilder) = appBuilder .UseRouting() .UseOxpecker(webApp) // Add Oxpecker to the ASP.NET Core pipeline, should go after UseRouting //.UseOxpecker(webApp1) will work //.UseOxpecker(webApp2) will also work |> ignore let configureServices (services: IServiceCollection) = services .AddRouting() .AddOxpecker() // Register default Oxpecker dependencies |> ignore [] let main _ = let builder = WebApplication.CreateBuilder(args) configureServices builder.Services let app = builder.Build() configureApp app app.Run() 0 ``` -------------------------------- ### Create EndpointHandler with Additional Parameters Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Add extra parameters before returning an existing EndpointHandler function to customize its behavior. ```fsharp let sayHelloWorld (name: string) : EndpointHandler = let greeting = sprintf "Hello World, from %s" name text greeting ``` -------------------------------- ### Adding Data Attributes with .data Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Demonstrates using the dedicated .data method to add data-* attributes to an HtmlElement. ```fsharp div().data("secret-key", "lk23j4oij234"){ "Secret div" } // renders
Secret div
``` -------------------------------- ### Add Metadata to Routes with `addMetadata` Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates adding metadata to a route using the `addMetadata` function, which can be utilized later in the request pipeline. ```fsharp let webApp = GET [ route "/foo" (text "Foo") |> addMetadata "foo" ] ``` -------------------------------- ### Apply Valueless x-transition Directive Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Alpine/README.md Renders the x-transition directive without any arguments, useful for applying default transition behavior. ```fsharp // Renders x-transition div().xTransition() { ... } ``` -------------------------------- ### Adding Event Handlers with .on Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Explains how to add JavaScript event handlers to an HtmlElement using the .on method, as Oxpecker.ViewEngine discourages direct attribute usage for events. ```fsharp div().on("click", "alert('Hello')"){ "Clickable div" } //
Clickable div
``` -------------------------------- ### Configure ASP.NET Core Response Caching Middleware Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Register the response caching middleware in your application's startup code before Oxpecker. The order of `AddResponseCaching` and `AddOxpecker` does not matter. ```fsharp let configureServices (services : IServiceCollection) = services .AddResponseCaching() // <-- Here the order doesn't matter .AddOxpecker() // This is just registering dependencies |> ignore let configureApp (app : IApplicationBuilder) = app .UseStaticFiles() // Optional if you use static files .UseAuthentication() // Optional if you use authentication .UseResponseCaching() // <-- Before UseOxpecker webApp .UseOxpecker webApp ``` -------------------------------- ### hx-ws: Connect and Send Form Data Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.Htmx/README.md Connects to a WebSocket and sends form data, enabling real-time chat functionality. ```fsharp div().hxWsConnect("/chatroom") { form().hxWsSend(true) { input(name="message") button(type'="submit") { "Send" } } } ``` -------------------------------- ### Write HTML View in Chunks with Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use `WriteHtmlViewChunked` or `htmlViewChunked` for streaming HTML content. This is useful for large views or when content is generated incrementally. ```fsharp let indexView = html() { head() { title() { "Oxpecker" } } body() { h1(id="Header") { "Oxpecker" } p() { "Hello World." } } } let someHandler : EndpointHandler = fun (ctx: HttpContext) -> task { // Do stuff return! ctx.WriteHtmlViewChunked indexView } // or... let someHandler : EndpointHandler = // Do stuff htmlViewChunked indexView ``` -------------------------------- ### Configure Response Caching with VaryByQueryKeys Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use this snippet to configure the response caching middleware to vary cached responses based on specific query keys. This is useful when query parameters should influence caching behavior. ```fsharp responseCaching (Some (CacheControlHeaderValue(MaxAge = TimeSpan.FromSeconds(30))) (Some "Accept, Accept-Encoding") (Some [| "query1"; "query2" |]) ``` -------------------------------- ### Configure Response Caching with Vary Headers Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Configure response caching with specific `vary` headers to ensure cached responses are only served when the specified request headers match. This is useful for content negotiation or compression. ```fsharp let cacheHeader = Some <| CacheControlHeaderValue(MaxAge = TimeSpan.FromSeconds(30), Public = true) // Cache for 30 seconds without any vary headers publicResponseCaching cacheHeader None None // Cache for 30 seconds with Accept and Accept-Encoding as vary headers publicResponseCaching cacheHeader (Some "Accept, Accept-Encoding") None ``` -------------------------------- ### Write HTML View with Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use `WriteHtmlView` or `htmlView` to compile an HTML view and write it to the response stream. Sets `Content-Length` and `Content-Type` headers automatically. ```fsharp let indexView = html() { head() { title() { "Oxpecker" } } body() { h1(id="Header") { "Oxpecker" } p() { "Hello World." } } } let someHandler : EndpointHandler = fun (ctx: HttpContext) -> task { // Do stuff return! ctx.WriteHtmlView indexView } // or... let someHandler : EndpointHandler = // Do stuff htmlView indexView ``` -------------------------------- ### Write JSON using WriteJson or json Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use `WriteJson` or `json` to serialize an object to JSON and write it to the response stream with appropriate headers. ```fsharp let someHandler (animal: Animal) : EndpointHandler = fun (ctx: HttpContext) -> task { // Do stuff return! ctx.WriteJson animal } // or... let someHandler (animal: Animal) : EndpointHandler = // Do stuff json animal ``` -------------------------------- ### addOpenApiSimple Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.OpenApi/README.md A shortcut method for simple OpenAPI cases. It infers the schema from the provided request and response generic types. ```APIDOC ## addOpenApiSimple ### Description This method is a shortcut for simple cases. It accepts two generic type parameters - request and response, so the schema can be inferred from them. ### Method Signature ```fsharp let addOpenApiSimple<'Req, 'Res> = ... ``` ### Usage Notes If your handler doesn't accept any input, you can pass `unit` as a request type (works for response as well). ``` -------------------------------- ### Redirection with redirectTo Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use the `redirectTo` endpoint handler to redirect a client to a different location. ```APIDOC ## redirectTo ### Description Redirects a client to a different location. ### Method Endpoint handler. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters for redirectTo - **location** (string) - The URL to redirect the client to. - **permanent** (bool) - If true, sends a 301 status code (permanent redirection); otherwise, sends a 302 status code (temporary redirection). ### Request Example ```fsharp let webApp = [ route "/new" <| text "Hello World" route "/old" <| redirectTo "https://myserver.com/new" true ] ``` ### Response #### Success Response (301 or 302) Redirects the client to the specified location. #### Response Example None provided. ``` -------------------------------- ### Bind Query Strings to Object using HTTP Handler Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Alternatively, use the `bindQuery<'T>` HTTP handler to bind query string parameters directly within the handler definition. The DTO must be decorated with `[]`. ```fsharp [] type Car = { Name : string Make : string Wheels : int Built : DateTime } let webApp = [ GET [ route "/" <| text "index" route "ping" <| text "pong" ] POST [ route "/car" (bindQuery (fun model -> %Ok model)) ] ] ``` -------------------------------- ### Configure CSRF Protection Middleware in Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Add `UseRouting`, `UseAntiforgery`, and `UseOxpecker` middleware in the correct order. Register `AddRouting`, `AddAntiforgery`, and `AddOxpecker` services. ```fsharp let configureApp (appBuilder: WebApplication) = appBuilder .UseRouting() .UseAntiforgery() // should be added between UseRouting and UseOxpecker .UseOxpecker(endpoints) |> ignore let configureServices (services: IServiceCollection) = services .AddRouting() .AddAntiforgery() .AddOxpecker() |> ignore ``` -------------------------------- ### Compose Routef with Bind* Operators Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use specialized operators like (<<), (<<+), and (<<++) to compose routef with bind* functions. These operators allow for binding query, form, or JSON data before executing the handler for parameterized routes. ```fsharp routef "/{%s}" (bindQuery << handler) ``` ```fsharp routef "/{%s}/{%s}" (bindForm <<+ handler) ``` ```fsharp routef "/{%s}/{%s}/{%s}" (bindJson <<++ handler) ``` -------------------------------- ### Bind Form using Endpoint Handler Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md An alternative way to bind form data using the bindForm http handler. The object type must be decorated with [CLIMutable]. ```fsharp [] type Car = { Name : string Make : string Wheels : int Built : DateTime } let webApp = [ GET [ route "/" <| text "index" route "ping" <| text "pong" ] POST [ route "/car" (bindForm (fun model -> %Ok model)) ] ] ``` -------------------------------- ### Response Caching Configuration Source: https://github.com/lanayx/oxpecker/blob/develop/MigrateFromGiraffe.md Shows the difference in configuring response caching between Giraffe and Oxpecker. Oxpecker uses a more structured approach with `CacheControlHeaderValue`. ```fsharp // Giraffe responseCaching (Public (TimeSpan.FromSeconds (float 5))) (Some "Accept, Accept-Encoding") (Some [| "query1"; "query2" |]) // Oxpecker responseCaching (Some <| CacheControlHeaderValue(MaxAge = TimeSpan.FromSeconds(5), Public = true)) (Some "Accept, Accept-Encoding") (Some [| "query1"; "query2" |]) ``` -------------------------------- ### Oxpecker.ViewEngine Core Interfaces Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Defines the core interfaces for HTML elements in Oxpecker.ViewEngine, including HtmlElement, HtmlTag, and HtmlContainer. ```fsharp type HtmlElement = abstract member Render: StringBuilder -> unit type HtmlTag = inherit HtmlElement abstract member AddAttribute: HtmlAttribute -> unit type HtmlContainer = inherit HtmlElement abstract member AddChild: HtmlElement -> unit ... ``` -------------------------------- ### Create EndpointHandler Accessing HttpContext Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md To access the HttpContext, explicitly return an EndpointHandler function that accepts HttpContext and returns a Task. ```fsharp let sayHelloWorld : EndpointHandler = fun (ctx: HttpContext) -> let name = ctx.TryGetQueryValue "name" |> Option.defaultValue "Oxpecker" let greeting = sprintf "Hello World, from %s" name text greeting ctx ``` -------------------------------- ### Bind Query Strings to Object using Extension Method Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Use the `BindQuery<'T>` extension method to bind query string parameters to an object of type 'T'. You can optionally provide a `CultureInfo` for parsing. ```fsharp [] type Car = { Name : string Make : string Wheels : int Built : DateTime } let submitCar : EndpointHandler = fun (ctx: HttpContext) -> // Binds the query string to a Car object let car = ctx.BindQuery() // or with a CultureInfo: let british = CultureInfo.CreateSpecificCulture("en-GB") let car2 = ctx.BindQuery(british) // Sends the object back to the client ctx.Write <| Ok car let webApp = [ GET [ route "/" <| text "index" route "ping" <| text "pong" route "/car" <| submitCar ] ] ``` -------------------------------- ### Set HTTP Status Code in Oxpecker Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker/README.md Demonstrates two methods for setting the HTTP status code of a response: using the `SetStatusCode` extension method or the `setStatusCode` function. ```fsharp let someHandler : EndpointHandler = fun (ctx: HttpContext) -> ctx.SetStatusCode 200 // Return a Task ``` ```fsharp let someHandler : EndpointHandler = setStatusCode 200 >=> text "Hello World" ``` -------------------------------- ### Adding Custom Attributes with .attr Source: https://github.com/lanayx/oxpecker/blob/develop/src/Oxpecker.ViewEngine/README.md Shows how to attach any custom attribute to an HtmlElement using the .attr method. ```fsharp div().attr("my-secret-key", "lk23j4oij234"){ "Secret div" } ```