Giter Club home page Giter Club logo

aspfeat's Introduction

AspFeat NuGet Status ASP.NET Core 6.0

A modular and low ceremony toolkit for ASP .Net and F#.

  • Modular injection of services and middlewares.
  • Set of low ceremony ready-to-use setups.
  • Functional helpers over ASP .Net and nothing else.
  • Focused on Web APIs.

You can find examples in the samples folder.

Startup

In order to setup a feature properly, it's necessary to first add the services to IServiceCollection and then use the middlewares with IApplicationBuilder. The downside is that they are mixed with other features and moreover the order of calls are important, which makes everything complicated.

To keep the startup clean, the idea is to package features into modules and then expose the setup of WebApplicationBuilder and WebApplication as a tuple.

The end result is that ASP .Net startup has never been so easy:

[<EntryPoint>]
let main args =
    let configure bld = uhttp bld Get "/" (write "hello world")
    WebApp.run args [ Endpoint.feat configure ]

Swagger sample:

module Swagger =
    open Microsoft.AspNetCore.Builder
    open Microsoft.Extensions.DependencyInjection

    let feat () : Feat =
        fun builder ->
            builder.Services
                .AddEndpointsApiExplorer()
                .AddSwaggerGen()
            |> ignore
        ,
        fun app ->
            app
                .UseSwagger()
                .UseSwaggerUI()
            |> ignore

[<EntryPoint>]
let main args =
    [ Endpoint.feat configureEndpoints
      Swagger.feat () ]
    |> WebApp.run args

Endpoint Routing

AspFeat comes with several helpers that ease the use of the functional programming paradigm.
We do not intend to completely change your way of using ASP .Net but rather to offer a more nice and more F#-idiomatic way of using ASP .Net.
So you are not limited to AspFeat and you can still use Vanilla ASP .Net, if you need to.

For further information please refer to Microsoft Docs

Example

Without AspFeat toolkit:

let configureEndpoints (bld: IEndpointRouteBuilder) =
    bld.MapGet("/", RequestDelegate getHandler) |> ignore

With AspFeat toolkit:

let configureEndpoints bld =
    uhttp bld Get "/" getHandler

With the DSL:

let configureEndpoints bld =
    endpoints bld {
        get "/" getHandler
    }

With the OpenApi/Swagger DSL:

let getHandler =
    writeAsJson "hello world"

type World =
    [<ProducesResponseType(StatusCodes.Status200OK)>]
    abstract member GetHandler: unit -> string

let configureEndpoints bld =
    endpointsMetadata<World> bld {
        get "/" getHandler (nameof getHandler)
    }

Route values and JSON content injection

Instead of manually fetching data through HttpContext, it is possible to inject them into the handler.

  • httpf / uhttpf injects route values.
    • A single value is injected as is.
    • Multiple values are injected in order as a tuple.
  • httpj / uhttpj injects the deserialized JSON content.
  • httpfj / uhttpfj combines both.
let hello firstname = write $"Hello {firstname}"
let createGift gift = write $"Create a {gift}"
let goodbye (firstname, lastname) gift =
    write $"Goodbye {firstname} {lastname} and here is your {gift}"

let configureEndpoints bld =
    uhttpf  bld Get  "/hello/{firstname}" hello
    uhttpj  bld Post "/gift" createGift
    uhttpfj bld Put  "/goodbye/{firstname}/{lastname}" goodbye

Http Handlers

Composition

It is possible to combine http handlers.

Those with input injection are railwayed with Result.

  • Ok type can be any value.
  • Error type is Map<string, string list> and the error response is a json of problem details with the status code 422 Unprocessable Entity.

Normal with =>

let enrich (ctx: HttpContext) =
    ctx.Response.GetTypedHeaders().Set("X-Powered-By", "AspFeat")
    Task.CompletedTask

let configureEndpoints bld =
    uhttp bld Get "/" (enrich => write "hello world")

Single value injection with =|

Ok type could be:

  • A route value
  • A tuple of route values
  • A deserialized JSON model
  • Or any mapped value
let validateGetEcho id ctx =
    if id > 0
    then Ok id
    else Map [ ("Id", [ "Is negative or zero" ]) ] |> Error
    |> Task.FromResult

let getEcho id =
    write $"Echo {id}"

let configureEndpoints bld =
    uhttpf bld Get  "/{id:int}" (validateGetEcho =| getEcho)

Double value injection with =||

Ok type is a two-value tuple that is then passed to the next function as two parameters.
It could be:

  1. Fist, route values
  2. Second, deserialized JSON model
let validateCreateEcho id name ctx =
    if not (String.IsNullOrWhiteSpace name)
    then Ok (id, {| Id = id; Name = name |})
    else Map [ ("Name", [ "Is empty" ]) ] |> Error
    |> Task.FromResult

let createEcho id model =
    //...
    createdWith $"/{id}" model

let configureEndpoints bld =
    uhttpfj bld Post "/{id:int}" (validateCreateEcho =|| createEcho)

aspfeat's People

Contributors

akhansari avatar natalie-o-perret avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

aspfeat's Issues

FSharp.OpenApi

what about the beautiful https://github.com/akhansari/FSharp.OpenApi
i see that has been archived, but i really needed only the OpenApi builder CE which looked awesome.

Could that be re-surrected? :) :) :)

or would it be possible to just generate a separate package for that only ?

i really liked the possibility to interop/integrat with existing F# frameworks (Giraffe/Saturn/Falco)

This project is awesome ! thanks !!! ๐Ÿ‘ ๐Ÿ” ๐Ÿ™ ๐Ÿคฉ ๐Ÿ˜Š

Idea / POC (?) for HttpContext and WebParts

ideas: we could maybe apply/chain at controller level webparts, using http context, that would make lots of Giraffe operators/interop possible?

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-6.0#use-httpcontext-from-a-controller

e.g.
use HttpContext to fetch dependencies..

[<Route("[controller]")>]
type ExampleController() =
    inherit Controller()

    [<HttpGet>]
    [<ProducesResponseType(StatusCodes.Status200OK, Type = typeof<MyDto>)>]
    member this.Get() =
        HttpContext
        |> httpHandlerOne
        >=> httpHandlerTwo
        >=> httpHandlerThree

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.