giraffe-fsharp / Giraffe

A native functional ASP.NET Core web framework for F# developers.
https://giraffe.wiki
Apache License 2.0
2.12k stars 267 forks source link

Questions about ASP.NET Core Middleware vs function composition #191

Closed alfonsogarciacaro closed 6 years ago

alfonsogarciacaro commented 6 years ago

Hi there! I'm starting ASP.NET Core and Giraffe and I have some confusion around the Middleware model vs simple function composition. If my understanding is correct, middleware is configured on startup and then the framework is responsible to call each middleware in order (though each can stop the chain simply by not calling next). However with function composition you must be explicit on the handler chain for each route (which should be preferred from a funcional programming point of view).

Giraffe's README starts saying that it integrates with Middleware pipe chain, but then samples seem to prefer function composition and I haven't found a case where the next function is actually called.

Would it be possible to add a section to documentation explaining the differences between ASP.NET Core middleware vs function composition, and a sample where it makes sense to call next? Thanks a lot in advance!

dustinmoris commented 6 years ago

Hi, sure no problem I can see where the confusion comes in.

Middleware is the lower level architecture of ASP.NET Core which allows composable web applications. While you could in theory write a whole app with middleware only, it is usually more favourable to plug a higher level framework (such as MVC or Giraffe) into the middleware and then handle all web app responsibilities from within that framework.

In MVC the higher level framework's architecture is an MVP pattern with controllers being the main way of building your web routes and stuff. In Giraffe the architecture is function composition which is VERY similar to middleware, because a Giraffe HttpHandler also has a next variable where each function in the pipeline has full control whether it wants to continue with the rest of the pipeline or short circuit a response.

So long answer short, in a typical Giraffe web application you would plug Giraffe into ASP.NET Core via middleware (app.UseGiraffe webApp) and then do the rest via function composition in Giraffe, because that is tailored to functional programming. However, there are some features which have already been solved by other ASP.NET middleware which Giraffe didn't have to duplicate, like static file handling. In those cases you would plug the static file middleware alongside giraffe and let the static file middleware handle all web requests to static files and then let giraffe do all your other dynamic stuff.

Not sure if my answer makes a lot of sense, but feel free to ask me more until it all clicks together, I'm happy to help :)

dustinmoris commented 6 years ago

Would it be possible to add a section to documentation explaining the differences between ASP.NET Core middleware vs function composition, and a sample where it makes sense to call next? Thanks a lot in advance!

The difference between ASP.NET Core middleware and the function composition in Giraffe is very slim. Giraffe is basically functional middleware. Think of it like ASP.NET Core middleware is the OO way of middleware and Giraffe took that concept (as it is very powerful) and then translated it into a much simpler functional variant (Giraffe is a functional copy of OO middleware). However, Giraffe doesn't really speak of itself as middleware, because it still wants to work alongside other existing middleware and therefore use the power of the entire ASP.NET Core eco system, but technically it is the same.

An example in Giraffe where it makes sense to call next is almost every default HttpHandler.

For example the GET handler will check if the incoming http request has the GET verb and if it does then it will call next so the next function afterwards can proceed. If it wasn't a GET request then it will not call next and short circuit, because there's no point in continuing with next if every function afterwards expects it to be a GET request.

This is also a main difference between Suave and Giraffe. In Giraffe every handler decides if it will call next so the next function in the composition can continue. In Suave each web part returns either Success or Failure and then a higher level function (the fish operator) decides based on the result if it will call the next function in the pipeline or not.

alfonsogarciacaro commented 6 years ago

Thanks a lot for your quick and detailed response, that's really helpful! I understand now where my confusion came from. My misunderstanding was to think the next in ASP.NET Core and Giraffe were the same thing, while they're actually different. I thought invoking next in Giraffe was actually calling the next ASP.NET Core middleware, but you have to return None for that.

Again, thanks for your explanations @dustinmoris! Everything starts to make sense now :+1:

dustinmoris commented 6 years ago

Perfect, glad I could help!