Giter Site home page Giter Site logo

giraffe-fsharp / giraffe Goto Github PK

View Code? Open in Web Editor NEW
2.1K 69.0 265.0 1.91 MB

A native functional ASP.NET Core web framework for F# developers.

Home Page: https://giraffe.wiki

License: Apache License 2.0

F# 99.96% HTML 0.04%
aspnet-core web-framework http-handler middleware fsharp micro-framework giraffe template-engine dotliquid dotnet

giraffe's Introduction

Giraffe

Giraffe

A functional ASP.NET Core micro web framework for building rich web applications.

Read this blog post on functional ASP.NET Core for more information.

NuGet Info

Linux, macOS and Windows Build Status

.NET Core

Windows Build history

Table of contents

About

Giraffe is an F# micro web framework for building rich web applications. It has been heavily inspired and is similar to Suave, but has been specifically designed with ASP.NET Core in mind and can be plugged into the ASP.NET Core pipeline via middleware. Giraffe applications are composed of so called HttpHandler functions which can be thought of a mixture of Suave's WebParts and ASP.NET Core's middleware.

If you'd like to learn more about the motivation of this project please read my blog post on functional ASP.NET Core (some code samples in this blog post might be outdated today).

Who is it for?

Giraffe is intended for developers who want to build rich web applications on top of ASP.NET Core in a functional first approach. ASP.NET Core is a powerful web platform which has support by Microsoft and a huge developer community behind it and Giraffe is aimed at F# developers who want to benefit from that eco system.

It is not designed to be a competing web product which can be run standalone like NancyFx or Suave, but rather a lean micro framework which aims to complement ASP.NET Core where it comes short for functional developers. The fundamental idea is to build on top of the strong foundation of ASP.NET Core and re-use existing ASP.NET Core building blocks so F# developers can benefit from both worlds.

You can think of Giraffe as the functional counter part of the ASP.NET Core MVC framework.

Getting Started

Using dotnet-new

The easiest way to get started with Giraffe is by installing the giraffe-template package, which adds a new template to your dotnet new command line tool:

dotnet new -i "giraffe-template::*"

Afterwards you can create a new Giraffe application by running dotnet new giraffe.

If you are using dotnet core 2.1.4, you will need to specify the language: dotnet new giraffe -lang F#

For more information about the Giraffe template please visit the official giraffe-template repository.

Doing it manually

Install the Giraffe NuGet package*:

PM> Install-Package Giraffe

*) If you haven't installed the ASP.NET Core NuGet package yet then you'll also need to add a package reference to Microsoft.AspNetCore.App:

PM> Install-Package Microsoft.AspNetCore.App

Alternatively you can also use the .NET CLI to add the packages:

dotnet add package Microsoft.AspNetCore.App
dotnet add package Giraffe

Next create a web application and plug it into the ASP.NET Core middleware:

open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.Logging
open Microsoft.Extensions.DependencyInjection
open Giraffe

let webApp =
    choose [
        route "/ping"   >=> text "pong"
        route "/"       >=> htmlFile "/pages/index.html" ]

type Startup() =
    member __.ConfigureServices (services : IServiceCollection) =
        // Register default Giraffe dependencies
        services.AddGiraffe() |> ignore

    member __.Configure (app : IApplicationBuilder)
                        (env : IHostEnvironment)
                        (loggerFactory : ILoggerFactory) =
        // Add Giraffe to the ASP.NET Core pipeline
        app.UseGiraffe webApp

[<EntryPoint>]
let main _ =
    Host.CreateDefaultBuilder()
        .ConfigureWebHostDefaults(
            fun webHostBuilder ->
                webHostBuilder
                    .UseStartup<Startup>()
                    |> ignore)
        .Build()
        .Run()
    0

Instead of creating a Startup class you can also add Giraffe in a more functional way:

open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.DependencyInjection
open Giraffe

let webApp =
    choose [
        route "/ping"   >=> text "pong"
        route "/"       >=> htmlFile "/pages/index.html" ]

let configureApp (app : IApplicationBuilder) =
    // Add Giraffe to the ASP.NET Core pipeline
    app.UseGiraffe webApp

let configureServices (services : IServiceCollection) =
    // Add Giraffe dependencies
    services.AddGiraffe() |> ignore

[<EntryPoint>]
let main _ =
    Host.CreateDefaultBuilder()
        .ConfigureWebHostDefaults(
            fun webHostBuilder ->
                webHostBuilder
                    .Configure(configureApp)
                    .ConfigureServices(configureServices)
                    |> ignore)
        .Build()
        .Run()
    0

For more information please check the official Giraffe documentation.

Sample applications

Demo apps

There is a few sample applications which can be found in the samples GitHub repository. Please check the README.md there for further information.

Live apps

buildstats.info

The web service https://buildstats.info uses Giraffe to build rich SVG widgets for Git repositories. The application runs as a Docker container in the Google Container Engine (see CI-BuiltStats on GitHub for more information).

dusted.codes

My personal blog https://dusted.codes is also built with Giraffe and ASP.NET Core and all of the source code is published on GitHub for further reference.

More sample applications will be added in the future.

Benchmarks

Giraffe is part of the TechEmpower Web Framework Benchmarks and will be listed in the official results page in the upcoming Round 17 for the first time.

Unofficial test results are currently available on the TFB Status page.

As of today Giraffe competes in the Plaintext, JSON and Fortunes categories and has been doing pretty well so far, even outperforming ASP.NET Core MVC in Plaintext and JSON at the time of writing.

The latest implementation which is being used for the benchmark tests can be seen inside the TechEmpower repository.

Giraffe is also featured in Jimmy Byrd's dotnet-web-benchmarks where we've run earlier performance tests.

Building and developing

Giraffe is built with the latest .NET Core SDK, which works on Windows, macOS and Linux out of the box.

You can either install Microsoft Visual Studio or JetBrains Rider which both come with the latest .NET Core SDK or manually download and install the .NET Core SDK and use the .NET CLI or Visual Studio Code with the Ionide extension to build and develop Giraffe.

The easiest way to build Giraffe is via the .NET CLI.

Run dotnet build from the root folder of the project to restore and build all projects in the solution:

dotnet build

Running dotnet test from the root of the project will execute all test projects referenced in the solution:

dotnet test

Contributing

Help and feedback is always welcome and pull requests get accepted.

TL;DR

  • First open an issue to discuss your changes
  • After your change has been formally approved please submit your PR against the develop branch
  • Please follow the code convention by examining existing code
  • Add/modify the README.md as required
  • Add/modify unit tests as required
  • Please document your changes in the upcoming release notes in RELEASE_NOTES.md
  • PRs can only be approved and merged when all checks succeed (builds on Windows and Linux)

Discuss your change first

When contributing to this repository, please first discuss the change you wish to make via an open issue before submitting a pull request. For new feature requests please describe your idea in more detail and how it could benefit other users as well.

Please be aware that Giraffe strictly aims to remain as light as possible while providing generic functionality for building functional web applications. New feature work must be applicable to a broader user base and if this requirement cannot be sufficiently met then a pull request might get rejected. In the case of doubt the maintainer might rather reject a potentially useful feature than adding one too many. This measure is to protect the repository from feature bloat and shall not be taken personally.

Code conventions

When making changes please use existing code as a guideline for coding style and documentation. For example add spaces when creating tuples ((a,b) --> (a, b)), annotating variable types (str:string --> str : string) or other language constructs.

Examples:

let someHttpHandler:HttpHandler =
    fun (ctx:HttpContext) next -> task {
        // Some work
    }

should be:

let someHttpHandler : HttpHandler =
    fun (ctx : HttpContext) (next : HttpFunc) ->
        task {
            // Some work
        }

Keep documentation and unit tests up to date

If you intend to add or change an existing HttpHandler then please update the README.md file to reflect these changes there as well. If applicable unit tests must be added or updated and the project must successfully build before a pull request can be accepted.

Submit a pull request against develop

The develop branch is the main and only branch which should be used for all pull requests. A merge into develop means that your changes are scheduled to go live with the very next release, which could happen any time from the same day up to a couple weeks (depending on priorities and urgency).

Only pull requests which pass all build checks and comply with the general coding guidelines can be approved.

If you have any further questions please let me know.

You can file an issue on GitHub or contact me via https://dusted.codes/about.

Nightly builds and NuGet feed

All official release packages are published to the official and public NuGet feed.

Nightly builds (builds from the develop branch) produce unofficial pre-release packages which can be pulled from the project's NuGet feed on GitHub.

These packages are being tagged with the Workflow's run number as the package version.

All other builds, such as builds triggered by pull requests produce a NuGet package which can be downloaded as an artifact from the individual GitHub action.

Blog posts

Blog posts by author

Community blog posts

If you have blogged about Giraffe, demonstrating a useful topic or some other tips or tricks then please feel free to submit a pull request and add your article to this list as a reference for other Giraffe users. Thank you!

Videos

License

Apache 2.0

Contact and Slack Channel

If you have any further questions feel free to reach out to me via any of the mentioned social media on https://dusted.codes/about or join the #giraffe Slack channel in the Functional Programming Slack Team. Please use this link to request an invitation to the Functional Programming Slack Team if you don't have an account registered yet.

Support

If you've got value from any of the content which I have created, but pull requests are not your thing, then I would also very much appreciate your support by buying me a coffee. Thank you!

Buy Me A Coffee

giraffe's People

Contributors

64j0 avatar banashek avatar baronfel avatar chadunit avatar colinbull avatar ctaggart avatar danieljsummers avatar danpozmanter avatar draganjovanovic1 avatar dustinmoris avatar dv00d00 avatar forki avatar gerardtoconnor avatar jchannon avatar joncanning avatar jtourlamain avatar kerams avatar kerryrj avatar krzysztof-cieslak avatar mcon avatar neftedollar avatar nhowka avatar nojaf avatar rslopes avatar slang25 avatar theangrybyrd avatar timdeschryver avatar vtquan avatar xdadaveshaw avatar zaid-ajaj 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  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  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  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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

giraffe's Issues

Creating the html in a functional way

I did a quick experiment with using https://github.com/SuaveIO/suave/blob/master/src/Experimental/Html.fs to construct the html.

let webApp = 
    choose [
        GET >=>
            choose [
                route "/" >=> razorView "Index.cshtml" { Text = "Hello, Giraffe world!" } ;
                route "/test" >=> setBodyAsString (Suave.Html.samplePage |> Suave.Html.htmlToString)
            ]

        setStatusCode 404 >=> text "Not Found" ]

Including the single Html.fs file in your project is enough to make this work. No need to reference the full Suave.Experimental nuget package.

Should Giraffe include something similar by default as alternative to Razor/dotliquid?

HttpHandler is inferred to be generic when in a different file

Starting from the dotnet Giraffe template, if I try to define any HttpHandler outside of the Program.fs file, the compiler complains that my HttpHandler functions are generic. Which is weird because code lens shows the correct signature.

image

If I annotate every thing to be HttpHandler it compiles fine, but I'm confused why I am required to do that here.

Serialization/Deserialization performance

According to this and other links serialization and deserialization should be done by using streams, not strings. And currently all helper extensions use strings. Was it intentional or this should be fixed?

Support query string binding for Option<'T>

When binding from a query string to a record with a field of type int option this exception happens.
Haven't try with form/json/xml binding.

System.AggregateException: One or more errors occurred. ---> System.NotSupportedException: TypeConverter cannot convert from System.String.
   at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
   at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at Giraffe.HttpContextExtensions.HttpContext-BindQueryString@86-1.Invoke(PropertyInfo p)
   at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source)
   at Giraffe.HttpContextExtensions.HttpContext-BindQueryString@82.Invoke(Unit unitVar)
   at [email protected](AsyncParams`1 args)
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.NotSupportedException: TypeConverter cannot convert from System.String.
   at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
   at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at Giraffe.HttpContextExtensions.HttpContext-BindQueryString@86-1.Invoke(PropertyInfo p)
   at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source)
   at Giraffe.HttpContextExtensions.HttpContext-BindQueryString@82.Invoke(Unit unitVar)
   at [email protected](AsyncParams`1 args)<---

Roadmap

Hi!
Will you provide roadmap?

Changes to Razor files don't seem to come through when the app is running.

When editing the Index.cshtml while the app is running, no changes seem to have any effect. The html output stays the same.

Steps to reproduce

  • Create a new app with dotnet new giraffe
  • Edit the Index.cshtml by adding some styling
@model yellow.Models.Message

<!DOCTYPE html>
<html>
<head>
    <title>Giraffe</title>
    <link href="https://fonts.googleapis.com/css?family=Lato:300" rel="stylesheet">
    <style>
        body {
            background-color:#f1c40f;
            color:#2c3e50;
            font-family: 'Lato', sans-serif;
        }

        h3{
            font-size: 4rem;
            font-weight: 300;
            text-align: center;
            /*border:1px solid blue;*/
        }
    </style>
</head>
<body>
    <div>
        <h3>@Model.Text</h3>
    </div>
</body>
</html>
  • run the app
  • uncomment the css
  • reload browser

The title doesn't seem to have a border.
I'm not sure if this is a Razor Engine issue or caching inside Giraffe.

Razor is not affected by the _ViewStart file

Razor uses ViewStart file to know what code to run before every views. I created the _ViewStart.cshtml file in my Views folder with the following code

@{
    Layout = "_Layout";
    ViewData["Title"] = "Home Page";
}

Neither parts affect how my view is rendered. You can't manually set the name of the ViewStart file in ASP.NET Core so I don't think that I am missing a set up step or giving it the wrong name either. Razor did noticed my _ViewImport.cshtml file, which is also a file that razor look for to set thing up, in the same location so it shouldn't be because of putting it in the wrong location.

Is it possible to realize advanced routing

Hi,

Giraffe looks really promising :).

I just wonder if it possible to realize a dynamic routing that allows to browse through directories.

E.g. a pattern like /browse/<some generic pattern that allows paths> that extracts the path dir1/dir2/dir2/file.ext from the request /browse/dir1/dir2/dir2/file.ext.

NancyFX for example supports regex pattern in URL templates.

Task computation expression instead of async

I know in general, from a development standpoint, using the async {} with F# is far easier but I have been playing around with this great project, and noticed that I am constantly having to Async.AwaitTask as everything async in asp.net core is done in Tasks not F# Async and got me wondering, would the task {} computation expression me better in this library over async so that all the parts interfaced with asp.net core without the overhead of converting back and forth between async all the time?

task {} computation expression is already part of FSharpX and further, it has been modified and performance improved by @buybackoff as he explains on StackOverflow, with his updated version on Github FSharpAsyncVsTPL
such that performance is much improved over async {}, especially for internal (non IO/Http) which is the bulk of the async work in the chained handlers.

I know it would take a bit of re-writing such that HttpHandler would be (HttpContext -> Task<'HttpContext option'>, as well as updating CE from async {} to task {}, but given the platform is focusing on performance, using asp.net core, it seems a shame to introduce fat that is less compatible with the server (and its extended services) and the Task CE is already built to handle the same use case ... just food for thought, I can help in re-write if everyone thought it made sense, luckily the project is young enough that such a breaking change would not cause too much havoc?

Development Environement

Is it worth updating the README with some suggestions on Developing? Although I'm still new to dotnet core apps I'm struggling to find the best way to work on this code.

Do people developer this in VS2015 / 17 or VS Code?

I'd assume VS Code given the lack of a SLN file. If it is, can the .vscode\tasks.json file be added?

Or is it all just command line?

Nesting route combinators

As a newcomer to AspNetCore.Lambda, I've been working on exploring the library. During this process, I attempted to do something like this:

let app =
  choose [
    route "/ping" >>= text "pong"
    route "/" >>= text "index"
    route "/api"
      >>= choose [
        route "" >>= setStatusCode 204 >>= text "No Content"
        route "/users" >>= text "users"
      ]
    route "/docs" >>= text "docs"
  ]

expecting a request to /api would return a 204 No Content response and a request to /api/users would return "users". To my surprise, I received a 404 Not Found for both. After doing some digging, I discovered that route tests if the given path matches the request path, so with route "/users" above, it compares a request to /api/users with the given /users, fails, and returns None.

I wasn't able to find anything in the existing code base to handle this, so I added the following as a helper module in my application to achieve the expected behavior:

let optionally pred value =
  if pred then Some value else None

let getCurrentRoot (ctx : HttpContext) : string =
  if ctx.Items.ContainsKey "rootPath"
  then ctx.Items.Item "rootPath" |> string
  else ""

let rootPath (part : string) =
  fun (ctx : HttpHandlerContext) ->
    let root = getCurrentRoot ctx.HttpContext
    ctx.HttpContext.Items.Item "rootPath" <- root + part
    ctx
    |> Some
    |> async.Return

let subPath (part : string) =
  fun (ctx : HttpHandlerContext) ->
    let fullPath = (getCurrentRoot ctx.HttpContext) + part
    ctx
    |> optionally (fullPath = ctx.HttpContext.Request.Path.ToString())
    |> async.Return

allowing the following to work as expected:

let app =
  choose [
    route "/ping" >>= text "pong"
    route "/" >>= text "index"
    rootPath "/api"
      >>= choose [
        subPath "" >>= setStatusCode 204 >>= text "No Content"
        subPath "/users" >>= text "users"
      ]
    route "/docs" >>= text "docs"
  ]

Does something like this exist today? If not, would it be useful to include this functionality as a part of AspNetCore.Lambda?

Router & Route Performance

@TheAngryByrd @dustinmoris @imetallica
This is a move of the topic over from Task CE issue. Just to summarise

  1. Current Giraffe router uses a process of elimination on a list of path substrings/patterns, iteratively testing everyone till match met which is elegant, simple but not performant.
  2. For performance, routes can be compiled into a tree structure that minimises the number of failed tested paths.
  3. The best tree structures are Tries (character/edge nodes) or Binary/AVL Tree (chunk/token tested higher/lower)
  4. I have initially tried trie as it only checks path char once while Bin/AVL checks same start path, multiple chars, multiple times, so if the trie node traversal can be sped up close to string checking, trie will be faster.
  5. I have built first draft of Trie Router and it reads path, char by char checking for node path and firing off Http Handler functions at appropriate endpoints.
  6. The initial draft (with exception of one route bug) has same perf for short routes, 20% faster on long, 50% on long parse routes, leading to blended 9% improvement on total.
  7. Custom made crawl/stream parsers are an important part of performance boost over standard regex.
  8. The initial draft uses ref obj nodes with dictionary edges, a crude and bulky way to initially test function, now that concept works, I will migrate nodes to structs with compressed sparse array edges in an effort to improve performance as the stack is faster than heap etc.
  9. You can note from perf table below, there is only a slight degradation of performance on Trie over longer routes while old router understandably is noticeably slower.
  10. I think the ultimate goal is that Giraffe router should be a lot faster than MVC such that Giraffe is the most compelling web platform when performance & composability are taken into account.

With all this said I will just re-iterate how Trie router Handler works vs standard route HttpHandler:

// (Node->Node) list -> HttpHandler
let routeTrie (routeFns:(Node->Node) list) = ...
// string HttpHandler -> (Node -> Node)
let routeT (path:string) (fn:HttpHandler) = fun (Node) -> Node
let (==>) (a: HttpHandler->Node->Node) (b:Httphandler) = a b 

let trieApi : HttpHandler =
    routeTrie [
        routeT "/" ==> text "Hello world, from Giraffe!"
        routeT "/test" ==> text "Giraffe test working"
        routeT "/about" ==> text "Giraffe about page!"
        routeTf "/data/%s/weather" (fun v -> sprintf "json (weatherForecasts (%s))" v |> text)
        routeTf "/value/%s" text 
        subRouteT "/auth" ==> AuthHandler >=>  routeTrie [
            routeT "/inbox" ==> text "Auth Inbox"
            routeTf "/parse%slong%istrings%sand%sIntegers" (fun (a,b,c,d) -> sprintf "%s | %i | %s | %s" a b c d |> text)
            routeTf "token/%s" (fun v -> text "following token recieved:" + v)                                    
            subRouteT "/manager" ==> routeTrie [
                routeT "/payroll" ==> text "Manager Payroll"
                routeTf "/team%ssales%f" (fun (t,s) -> sprintf "team %s had sales of %f" t s |> text)
                routeTf "/accesscode/%i" (fun i -> sprintf "manager access close is %i" i |> text)
                subRouteT "/executive" ==> routeTrie [
                    routeT "/finance" ==> text "executive finance"
                    routeTf "/area/%s" (sprintf "executive area %s" >> text)
                    routeTf "/area/%s/district/%s/costcode%i" (fun (a,d,c) -> sprintf "executive area %s district %s costcode %s"  a d c |> text)
                 ]
            ]
        ]
    ]

routeTrie compiles the list of route mapping functions into its root node that then represents the pre-complied route Trie for requests/paths to traverse. The important part is to do as much work as possible in the compile stage such that compute time at runtime matching is minimised.

URLS GiraffeTask GiraffeTaskTrie Task/Trie
/ 2370840 2349487 -1%
/about 2391520 2324699 -3%
/auth/dashboard 2325389 2316402 0%
/auth/helpdesk 2322608 2303590 -1%
/auth/inbox 2317181 2320119 0%
/auth/manager/accesscode/57646878 1792291 2271399 27%
/auth/manager/executive/area/globaloperationcentral 1617520 2301895 42%
/auth/manager/executive/finance 1930551 2311994 20%
/auth/manager/executive/mis 1963573 2320318 18%
/auth/manager/executive/operations 1954403 2337297 20%
/auth/manager/payroll 2211415 2306816 4%
/auth/manager/teambravosales5345545.34544 1365431 2224991 63%
/auth/manager/teamview 2188016 2342409 7%
/auth/manager/timesheets 2199281 2316064 5%
/auth/parsefirstlong987654321stringssecondandthirdIntegers 1503244 2264492 51%
/authtoken/as8d7f098adsf897asdf7a09dfasd7f9as7df987 2088203 2287279 10%
/data/sunnydrycold/weather 2304337 2287102 -1%
/delivery 2322192 2343339 1%
/ourstory 2342938 2370148 1%
/products 2339945 2353792 1%
/test 2377866 2374676 0%
/value/thisisavaluethatisbeingpassed 2295704 2306295 0%
/wheretofindus 2332918 2368049 2%
Grand Total 48857366 53302652 9%

Perf numbers represent total req per second over seperate 3 runs ( /3 for expected perf)

The name

AspNetCore.Lambda would be a great name, however there is a lot of buzz at the moment about AWS Lambda, and it's recent .NET Core support (and even F# support!). I wonder if this confusion is limited to me and the people I surround myself with as I am working in a company using AWS heavily, or if this is a wider problem.

Here's a quick list just to illustrate what people might get in a google search:

I wonder what your feeling is on the name, and given that this is in alpha, is this already set in stone for you?

Of course, I could be totally wrong about this.

Also you had a shout out on the ASP.NET Community Standup @ 23:30, I think Damian Edwards is right and an ASP.NET Core template needs to come from the community and I would love to see this library be be part of that template, getting the name right would go a long way towards that IMO.

Swagger support

Is it possible to make use of existing swagger and JWT middleware in Giraffe.
Just started to look at this project and will appreciate if you can speed me up.

Thank you.

Breaking changes between Giraffe 0.1.0-alpha-025 and 0.1.0-beta001

I don't know if this was intended, but based on the template of few days ago when I try compile my project with the new version, I got:

"/home/lerax/Desktop/dchart/src/Backend/Backend.fsproj" (default target) (1) ->
(CoreCompile target) -> 
  /home/lerax/Desktop/dchart/src/Backend/Server.fs(40,13): error FS0001: Type mismatch. Expecting a�    'HttpContext -> 'a'    �but given a�    'HttpFunc -> 'b -> HttpFuncResult'    �The type 'HttpContext' does not match the type 'HttpFunc' [/home/lerax/Desktop/dchart/src/Backend/Backend.fsproj]
  /home/lerax/Desktop/dchart/src/Backend/Server.fs(40,13): error FS0001: Type mismatch. Expecting a�    'HttpContext -> 'a'    �but given a�    'HttpFunc -> 'b -> HttpFuncResult'    �The type 'HttpContext' does not match the type 'HttpFunc' [/home/lerax/Desktop/dchart/src/Backend/Backend.fsproj]
  /home/lerax/Desktop/dchart/src/Backend/Server.fs(47,32): error FS0001: The type 'HttpFunc' is not compatible with the type 'HttpContext' [/home/lerax/Desktop/dchart/src/Backend/Backend.fsproj]

Running through 0.1.0-alpha-025 I got compiling fine, I just want know what is design changes on the new beta.

Where the source is basically the template default:
image

Continuations over binding Httphandlers (Breaking Change)

As hinted to before, I would propose we change the HttpHandler format while it is still in alpha/beta, while we still can, to use a continuation format rather than a binding format.

To explain why (and not drown this thread, I have blogged small piece here:
Carry On! … Continuation over binding pipelines for functional web

The overall return format would remain the same, returning Task<HttpContext option> but HttpHandlers would now include a continuation parameter next such that this is called rather than Some when an evaluation wants to move to the next handler in the pipeline.

Examples:

type HttpHandlerResult = Task<HttpContext option> // same as previous 

type HttpCont = HttpContext -> HttpHandlerResult  // continuation same as previous HttpHandler 

type HttpHandler = HttpCont -> HttpContext -> HttpHandlerResult // (HttpCont -> HttpCont) : Handler now has two input parameters 

let compose (handler : HttpHandler) (handler2 : HttpHandler) : HttpHandler =
    fun (next:HttpCont) (ctx : HttpContext) ->
        // 1. last next will ultimately be a (fun ctx -> Some ctx) but allows to compose / short circuit with continuations
        // 2. We can apply next function at load so all handlers have thier next function refs and need only apply ctx on run 
        let child  = handler2 next // suffix child handler(s) take next (Some ctx) success function
        let next = handler child   // prefix parent handler takes child handler as next continuation function 
        match ctx.Response.HasStarted with
        | true  -> task { return Some ctx }    // short circuit case
        | false -> next ctx
            
let rec choose (handlers : HttpHandler list) : HttpHandler =    
    fun next ctx ->
        task {
            match handlers with
            | []              -> return None
            | handler :: tail ->
                let! result = handler next ctx
                match result with
                | Some c -> return Some c    // bind wrapping required for choose handler (branching case)
                | None   -> return! choose tail next ctx
        }

let httpVerb (verb : string) : HttpHandler =
    fun next ctx ->
        if ctx.Request.Method.Equals verb
        then next ctx           // no longer wrapping async ('Some' replaced with 'next' fn)
        else task { return None }
        
let route (path : string) : HttpHandler =
    fun next ctx ->
        if (getPath ctx).Equals path
        then next ctx           // no longer wrapping async ('Some' replaced with 'next' fn)
        else Task.FromResult None

Websockets suaveio way?

Hi!
Giraffe expose very useful suaveio-like api for .net core, will you go further and provide same socket api?

Swagger support (or Swashbuckle compatibility)

It doesn't apparently work out of the box, as I expected.

Would it be impossible by design to make Giraffe work alongside Swashbuckle.AspNetCore?

According to the docs, it relies on ApiExplorer which is a subset of Mvc.Core. But maybe it's possible to make it work by implementing an ApiDescription or something?

I'm mostly posting this issue because (a) automatic documentation is a really cool feature, and (b) I get a gut feeling that, if the implementation is possible at all, it's probably more possible while this framework is still in beta.

Otherwise, Swagger integration could be achieved in a 'bolted-on' way similar to Suave's (tl;dr: make wrapper combinators that contains both the documentation and the WebPart, then expose the WebPart as a property).

Bind operator >>= vs >=>

Not an issue, but question.

Suave took the decision in late 2015 to change its bind operator from >>= to >=> for reasons.

Was it a conscious choice to not follow this design?

warbler not working

I followed the exact example that there is on the site using warbler but I am getting page as a static result.

let time() =
    System.DateTime.Now.ToString()

let webApp =
    choose [
        GET >=>
            choose [
                route "/once"      >=> (time() |> text)
                route "/everytime" >=> warbler (fun _ -> (time() |> text))
            ]
    ]

Deploying to Azure App Service

I had to slightly modify the base Giraffe template in order to successfully deploy it to an Azure App Service:

  1. (Optional) In the Azure Portal, first clear out all the "Default Documents" in the "Application Settings" for the targeted App Service. May also need to delete them from wwwroot.
  2. Added a PackageReference to the fsproj for Microsoft.AspNetCore.Server.IISIntegration (1.1.*)
  3. Added UseIISIntegration() to the WebHostBuilder init
  4. Published the app with a web.config similar to the one below (alongside app DLL; app DLL name will vary)

<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=".\giraffe.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" /> </system.webServer> </configuration>

With those changes, anyone should be able to follow this ASP.NET Core Azure tutorial and deploy the giraffe-template Nuget pkg to their Azure App Service.

Caveats: Not really an ASP.NET Core expert yet, so I'll let you decide how to manage that web.config situation. It gets generated into "PubTmp" if you publish a C# ASP.NET Core app from VS2017, so I'm not sure at what stage it'd be created in a vscode workflow (dotnet publish doesn't appear to generate it). Also, it's possible to directly host w/ Kestrel on Azure, but general rec is to use IIS/Nginx as a reverse proxy.

.NET Core SDK 2.0 is supported or will be?

Description

For now the README.md seems to point that Giraffe is design to build on top of the lastest released SDK, which for now is the SDK 1.0 and runtime 1.1. I tried, naively build the provided template with the SDK 2.0.0 preview 2 and doesn't works. So, for now, this is the expected, right?

But you have plans to support SDK 2.0.0 before release too? Or only after the official release?

Thanks anyway for that great web framework!

System/SDK version

Just for completeness sake, I'll add my current version info from dotnet --info

❯ dotnet --info
.NET Command Line Tools (2.0.0-preview2-006497)

Product Information:
 Version:            2.0.0-preview2-006497
 Commit SHA-1 hash:  06a2093335

Runtime Environment:
 OS Name:     manjaro
 OS Version:  
 OS Platform: Linux
 RID:         linux-x64
 Base Path:   /opt/dotnet/sdk/2.0.0-preview2-006497/

Microsoft .NET Core Shared Framework Host

  Version  : 2.0.0-preview2-25407-01
  Build    : 40c565230930ead58a50719c0ec799df77bddee9

The logo

Hey everyone,

I set up a design contest for the Giraffe logo and I have filtered down all logos to what I believe are the 3 best options so far and I would like you to help me pick the right logo!

All 3 designers still have a bit more than a day to make any changes to the logos. If you really like a logo, but think it deserves a few more changes then please let me know and I will forward the feedback.

These are the options:

image

Thanks for helping!

Build on linux

This project should also have a build on travis for linux mono/dotnetcore. I'll submit a PR with the travis file and build.sh Probably don't need all of these but a small subset like mono 5.0 and 4.8 https://github.com/TheAngryByrd/Chessie.Hopac/blob/master/.travis.yml

I know dotnet test fails on mono microsoft/vstest#445 microsoft/vstest#679 however if we use dotnet xunit https://www.nuget.org/packages/dotnet-xunit/2.3.0-beta2-build3683 I added support for mono awhile ago xunit/xunit#1213

Websockets, Channels, PubSub and Presence

Hi everyone, I'm up for grabs to a Websocket + PubSub + Presence implementation for Giraffe. I think that, with the needs of realtime web, and from my experience seeing people coming from Rails to Elixir + Phoenix Framework for realtime + performance, it's something we should consider to have an implementation in Giraffe out of the box.

So I want to open a discussion on the idea. My initial roadmap is to have the following:

  1. Add websockets + channels to the routing layer of Giraffe.
  2. Create a JS library to handle the channel layer.
  3. Add support for PubSub.
  4. Add presence on top of PubSub.

For someone who is not familiar with Channels, the ideia is that we can route messages to specific topic:subtopic, much in a way an URL route is, so you can run authentication/authorization rules specific for each channel.

For the PubSub layer, well, if you are running multiple instances of Giraffe, you need a way to broadcast to all connected clients to that channel, independently if they are connected to node 1 or 2. So, my idea is to have an in-memory backend for testing purposes and an Redis backend. Maybe use the Microsoft.Extensions.Caching.* namespace (?). Probably need some advice here. :)

For the Presence layer, the idea is to have a CRDT, so you can, for example, count the amount of devices for account X, connected in the channel, at the same time. For example, if John is connected both on his phone and on a web browser, it should show John only once. Or you can just show John <mobile> <desktop>, for example. The sky is the limit. :)

Thoughts?

Add logging to default handlers

Add logging for different log levels to help diagnose issues if they occur.

In ASP.NET Core middleware and other infrastructure code logs a lot of debug information when the log level is turned up. This is helpful when someone tries to diagnose low level issues such as routing for example.

ASP.NET Core Lambda should also log key events in the default HttpHandler functions when debug level is turned on.

Question: Get header when using routef

If my route matches I want to return a response, but to compose the correct response I need to check a header value. How would I do this using routef?

let app = 
    choose [
        routef "/bar/%s/%i" (fun (name, age) ->
            // I need a header value
            let myHeader = ???
            text (sprintf "Name: %s, Age: %i, Header: %s" name age myHeader))
    ]

My current workaround is getting the header first and then pipe the routef function.

let example (ctx: HttpHandlerContext) = 
    async {
        let header = ctx.HttpContext.Request.Headers.["barba-x"].ToString()
        return! ctx |> routef "/contact/%s" (fun id -> 
                    let response = sprintf "Id: %s, Header: %s" id header
                    text response
                )
    }

let webApp = 
    choose [
        GET >=>
            choose [
                example
            ]
        setStatusCode 404 >=> text "Not Found" ]

Would this be the recommended way?

Update to VS 2017 build image in AppVeyor when available

Currently the build has to manually pull and install the latest .NET Core SDK (RC4 at the time of writing), which makes the builds very slow.

Upgrade to a build image which has the latest .NET Core SDK pre-installed as soon as it becomes available.

Copy paste error in readme (webapp)

This looks great!

First time reading through your fantastic readme I noticed in the "clearResponse" section a reference to a non-existent webApp variable in the last line of the example. The app itself is called "app", which of course clashes with the application builder parameter. I notice that the "Doing it manually" section calls it webApp - maybe it got copied from there.

Installing Giraffe on the full .NET framework

I'm not entirely sure this is a problem with Giraffe but I'm unable to install Giraffe on a full framework project using .NET 4.6.1. Which according to this table also should support Net standard 1.6.

Install-Package : Could not install package 'Giraffe 0.1.0-alpha015'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.6.1', but the package does not contain any assembl
y references or content files that are compatible with that framework. For more information, contact the package author.
At line:1 char:16
+ Install-Package <<<<  Giraffe -Pre 
    + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand

It is also unclear to me what the with tooling 2.0 bit is in the table.

Parameter-less Function is Treated as a Value when Composed

If I compose a parameter-less function, route "/" >=> noParameterFunction () for example, my function is being evaluated on start and treated as a value.

To reproduce, start out with a new project from the template make the modification below

let noParameterFunction () =
    printfn "Hello"
    text "Hello"


let webApp = 
    choose [
        GET >=>
            choose [
                route "/" >=> noParameterFunction ()
            ]
        setStatusCode 404 >=> text "Not Found" ]

If you call dotnet run, "Hello" will be printed to console without going to the route. Going to the route will not call the printfn statement again.

Restoring the route code back to route "/" >=> razorHtmlView "Index" { Text = "Hello world, from Giraffe!" } while keeping the function and "Hello" will not be printed after dotnet run

Move Giraffe.Tasks into separate NuGet package?

Since there is a lot of lower level libraries which then would get used higher up from a Giraffe web application they will probably need/want to expose endpoints which return a Task as well instead of an Async if there is some asynchronous work to be done.

In this instance it might make more sense to only have to reference a smaller NuGet package which only exposes the task {} workflow rather than having to reference the entire Giraffe web framework.

I think this will become a common use case, so should think about it?

What do people think?

/cc @gerardtoconnor

Gitter

No idea how hard this is but I was wondering if we could benefit from a gitter channel?

dotnet run fails to run project

Hi
I created a sample project using dotnet new giraffe
After this i used dotnet restore
When i do dotnet run, i get the following error;

PS C:\Users\Mockingbird\DevSpace\DotNet\giraffe> dotnet run
C:\Users\Mockingbird\DevSpace\DotNet\giraffe\Program.fs(62,10): error FS0039: The field, constructor or member 'UseWebRoot' is not defined. [C:\Users\Mockingbird\DevSpace\DotNet\giraffe\DotNet.fsproj]

The build failed. Please fix the build errors and run again.

my dotnet version is 1.0.4
I am on windows 10, 64 bits

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.