Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
dustinmoris committed Sep 1, 2020
2 parents 37e69a5 + 3c7824b commit fa3a867
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 47 deletions.
6 changes: 3 additions & 3 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ The `task {}` CE is an independent project maintained by [Robert Peele](https://

**IMPORTANT NOTICE**

If you have `do!` bindings in your Giraffe web application then you must open the `FSharp.Control.Tasks.ContextInsensitive` namespace to resolve any type inference issues:
If you have `do!` bindings in your Giraffe web application then you must open the `FSharp.Control.Tasks.V2.ContextInsensitive` namespace to resolve any type inference issues:

```fsharp
open FSharp.Control.Tasks.V2.ContextInsensitive
Expand Down Expand Up @@ -721,7 +721,7 @@ You can also set an HTTP header via the `setHttpHeader` http handler:

```fsharp
let notFoundHandler : HttpHandler =
setHttpHeader "X-CustomHeader"
setHttpHeader "X-CustomHeader" "Some value"
>=> RequestErrors.NOT_FOUND "Not Found"
let webApp =
Expand Down Expand Up @@ -2959,7 +2959,7 @@ let configureServices (services : IServiceCollection) =
// Optionally use `FSharp.SystemTextJson` (requires `FSharp.SystemTextJson` package reference)
serializationOptions.Converters.Add(JsonFSharpConverter(JsonUnionEncoding.FSharpLuLike))
// Now register SystemTextJsonSerializer
this.AddSingleton<IJsonSerializer>(SystemTextJsonSerializer(SystemTextJsonSerializer.DefaultOptions)) |> ignore
this.AddSingleton<IJsonSerializer>(SystemTextJsonSerializer(serializationOptions)) |> ignore
```


Expand Down
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Release Notes
=============

## 5.0.0-alpha-003

- Enhanced Endpoint routing with a metadata list (see [PR #437](https://github.com/giraffe-fsharp/Giraffe/pull/437))

## 5.0.0-alpha-002

- Swapped Markdown docs for XML docs for all functions.
Expand Down
79 changes: 44 additions & 35 deletions src/Giraffe/EndpointRouting.fs
Original file line number Diff line number Diff line change
Expand Up @@ -179,24 +179,25 @@ type HttpVerb =

type RouteTemplate = string
type RouteTemplateMappings = list<string * char>
type MetadataList = obj list

type Endpoint =
| SimpleEndpoint of HttpVerb * RouteTemplate * HttpHandler
| TemplateEndpoint of HttpVerb * RouteTemplate * RouteTemplateMappings * (obj -> HttpHandler)
| NestedEndpoint of RouteTemplate * Endpoint list
| SimpleEndpoint of HttpVerb * RouteTemplate * HttpHandler * MetadataList
| TemplateEndpoint of HttpVerb * RouteTemplate * RouteTemplateMappings * (obj -> HttpHandler) * MetadataList
| NestedEndpoint of RouteTemplate * Endpoint list * MetadataList

let inline (=>) (fx : Endpoint -> Endpoint) (x : Endpoint) = fx x

let rec private httpVerb
(verb : HttpVerb)
(endpoint : Endpoint) : Endpoint =
match endpoint with
| SimpleEndpoint (_, routeTemplate, requestDelegate) ->
SimpleEndpoint (verb, routeTemplate, requestDelegate)
| TemplateEndpoint(_, routeTemplate, mappings, requestDelegate) ->
TemplateEndpoint(verb, routeTemplate, mappings, requestDelegate)
| NestedEndpoint (routeTemplate, endpoints) ->
NestedEndpoint (routeTemplate, endpoints |> List.map (httpVerb verb))
| SimpleEndpoint (_, routeTemplate, requestDelegate, metadata) ->
SimpleEndpoint (verb, routeTemplate, requestDelegate, metadata)
| TemplateEndpoint(_, routeTemplate, mappings, requestDelegate, metadata) ->
TemplateEndpoint(verb, routeTemplate, mappings, requestDelegate, metadata)
| NestedEndpoint (routeTemplate, endpoints, metadata) ->
NestedEndpoint (routeTemplate, endpoints |> List.map (httpVerb verb), metadata)

let GET = httpVerb GET
let POST = httpVerb POST
Expand All @@ -211,7 +212,7 @@ let CONNECT = httpVerb CONNECT
let route
(path : string)
(handler : HttpHandler) : Endpoint =
SimpleEndpoint (HttpVerb.NotSpecified, path, handler)
SimpleEndpoint (HttpVerb.NotSpecified, path, handler, [])

let routef
(path : PrintfFormat<_,_,_,_, 'T>)
Expand All @@ -220,68 +221,76 @@ let routef
let boxedHandler (o : obj) =
let t = o :?> 'T
routeHandler t
TemplateEndpoint (HttpVerb.NotSpecified, template, mappings, boxedHandler)
TemplateEndpoint (HttpVerb.NotSpecified, template, mappings, boxedHandler, [])

let subRoute
(path : string)
(endpoints : Endpoint list) : Endpoint =
NestedEndpoint (path, endpoints)
NestedEndpoint (path, endpoints, [])

let rec applyBefore
(httpHandler : HttpHandler)
(endpoint : Endpoint) =
match endpoint with
| SimpleEndpoint(v, p, h) -> SimpleEndpoint(v, p, httpHandler >=> h)
| TemplateEndpoint(v, p, m, h) -> TemplateEndpoint(v, p, m, fun (o: obj) -> httpHandler >=> h o)
| NestedEndpoint(t, lst) -> NestedEndpoint(t, List.map (applyBefore httpHandler) lst)
| SimpleEndpoint(v, p, h, ml) -> SimpleEndpoint(v, p, httpHandler >=> h, ml)
| TemplateEndpoint(v, p, m, h, ml) -> TemplateEndpoint(v, p, m, (fun (o: obj) -> httpHandler >=> h o), ml)
| NestedEndpoint(t, lst, ml) -> NestedEndpoint(t, List.map (applyBefore httpHandler) lst, ml)

let rec applyAfter
(httpHandler : HttpHandler)
(endpoint : Endpoint) =
match endpoint with
| SimpleEndpoint(v, p, h) -> SimpleEndpoint(v, p, h >=> httpHandler)
| TemplateEndpoint(v, p, m, h) -> TemplateEndpoint(v, p, m, fun (o: obj) -> h o >=> httpHandler)
| NestedEndpoint(t, lst) -> NestedEndpoint(t, List.map (applyAfter httpHandler) lst)
| SimpleEndpoint(v, p, h, ml) -> SimpleEndpoint(v, p, h >=> httpHandler, ml)
| TemplateEndpoint(v, p, m, h, ml) -> TemplateEndpoint(v, p, m, (fun (o: obj) -> h o >=> httpHandler), ml)
| NestedEndpoint(t, lst, ml) -> NestedEndpoint(t, List.map (applyAfter httpHandler) lst, ml)

let rec addMetadata
(metadata: obj)
(endpoint: Endpoint) =
match endpoint with
| SimpleEndpoint(v, p, h, ml) -> SimpleEndpoint(v, p, h, metadata::ml)
| TemplateEndpoint(v, p, m, h, ml) -> TemplateEndpoint(v, p, m, h, metadata::ml)
| NestedEndpoint(t, lst, ml) -> NestedEndpoint(t, lst, metadata::ml)

// ---------------------------
// Middleware Extension Methods
// ---------------------------

type IEndpointRouteBuilder with

member private this.MapSingleEndpoint (singleEndpoint : HttpVerb * RouteTemplate * RequestDelegate) =
let verb, routeTemplate, requestDelegate = singleEndpoint
member private this.MapSingleEndpoint (singleEndpoint : HttpVerb * RouteTemplate * RequestDelegate * MetadataList) =
let verb, routeTemplate, requestDelegate, metadataList = singleEndpoint
match verb with
| NotSpecified -> this.Map(routeTemplate, requestDelegate) |> ignore
| _ -> this.MapMethods(routeTemplate, [ verb.ToString() ], requestDelegate) |> ignore
| NotSpecified -> this.Map(routeTemplate, requestDelegate).WithMetadata(List.toArray metadataList) |> ignore
| _ -> this.MapMethods(routeTemplate, [ verb.ToString() ], requestDelegate).WithMetadata(List.toArray metadataList) |> ignore

member private this.MapNestedEndpoint (nestedEndpoint : RouteTemplate * Endpoint list) =
let subRouteTemplate, endpoints = nestedEndpoint
member private this.MapNestedEndpoint (nestedEndpoint : RouteTemplate * Endpoint list * MetadataList) =
let subRouteTemplate, endpoints, parentMetadata = nestedEndpoint
let routeTemplate = sprintf "%s%s" subRouteTemplate
endpoints
|> List.iter (
fun endpoint ->
match endpoint with
| SimpleEndpoint (v, t, h) ->
| SimpleEndpoint (v, t, h, ml) ->
let d = RequestDelegateBuilder.createRequestDelegate h
this.MapSingleEndpoint(v, routeTemplate t, d)
| TemplateEndpoint(v, t, m, h) ->
this.MapSingleEndpoint(v, routeTemplate t, d, ml @ parentMetadata)
| TemplateEndpoint(v, t, m, h, ml) ->
let d = RequestDelegateBuilder.createTokenizedRequestDelegate m h
this.MapSingleEndpoint(v, routeTemplate t, d)
| NestedEndpoint (t, e) ->
this.MapNestedEndpoint(routeTemplate t, e)
this.MapSingleEndpoint(v, routeTemplate t, d, ml @ parentMetadata)
| NestedEndpoint (t, e, ml) ->
this.MapNestedEndpoint(routeTemplate t, e, ml @ parentMetadata)
)

member this.MapGiraffeEndpoints (endpoints : Endpoint list) =
endpoints
|> List.iter(
fun endpoint ->
match endpoint with
| SimpleEndpoint (v, t, h) ->
| SimpleEndpoint (v, t, h, ml) ->
let d = RequestDelegateBuilder.createRequestDelegate h
this.MapSingleEndpoint (v, t, d)
| TemplateEndpoint(v, t, m, h) ->
this.MapSingleEndpoint (v, t, d, ml)
| TemplateEndpoint(v, t, m, h, ml) ->
let d = RequestDelegateBuilder.createTokenizedRequestDelegate m h
this.MapSingleEndpoint(v, t, d)
| NestedEndpoint (t, e) -> this.MapNestedEndpoint (t, e)
this.MapSingleEndpoint(v, t, d, ml)
| NestedEndpoint (t, e, ml) -> this.MapNestedEndpoint (t, e, ml)
)
18 changes: 9 additions & 9 deletions src/Giraffe/ResponseCaching.fs
Original file line number Diff line number Diff line change
Expand Up @@ -70,25 +70,25 @@ let responseCaching (directive : CacheDirective)
let noResponseCaching : HttpHandler = responseCaching NoCache None None

/// <summary>
/// Enables response caching for clients and proxy servers.
/// This http handler integrates with ASP.NET Core's response caching middleware.
///
/// The <see cref="responseCaching"/> http handler will set the relevant HTTP response headers in order to enable response caching on the client, by proxies and by the ASP.NET Core middleware (if enabled).
/// Enables response caching for clients only.
///
/// The <see cref="responseCaching"/> http handler will set the relevant HTTP response headers in order to enable response caching on the client only.
/// </summary>
/// <param name="seconds">Specifies the duration (in seconds) for which the response may be cached.</param>
/// <param name="vary">Optionally specify which HTTP headers have to match in order to return a cached response (e.g. `Accept` and/or `Accept-Encoding`).</param>
/// <param name="vary">Optionally specify which HTTP headers have to match in order to return a cached response (e.g. Accept and/or Accept-Encoding).</param>
/// <returns>A Giraffe <see cref="HttpHandler"/> function which can be composed into a bigger web application.</returns>
let privateResponseCaching (seconds : int) (vary : string option) : HttpHandler =
responseCaching (Private (TimeSpan.FromSeconds(float seconds))) vary None

/// <summary>
/// Enables response caching for clients only.
/// Enables response caching for clients and proxy servers.
/// This http handler integrates with ASP.NET Core's response caching middleware.
///
/// The <see cref="responseCaching"/> http handler will set the relevant HTTP response headers in order to enable response caching on the client, by proxies and by the ASP.NET Core middleware (if enabled).
///
/// The <see cref="responseCaching"/> http handler will set the relevant HTTP response headers in order to enable response caching on the client only.
/// </summary>
/// <param name="seconds">Specifies the duration (in seconds) for which the response may be cached.</param>
/// <param name="vary">Optionally specify which HTTP headers have to match in order to return a cached response (e.g. Accept and/or Accept-Encoding).</param>
/// <param name="vary">Optionally specify which HTTP headers have to match in order to return a cached response (e.g. `Accept` and/or `Accept-Encoding`).</param>
/// <returns>A Giraffe <see cref="HttpHandler"/> function which can be composed into a bigger web application.</returns>
let publicResponseCaching (seconds : int) (vary : string option) : HttpHandler =
responseCaching (Public (TimeSpan.FromSeconds(float seconds))) vary None
responseCaching (Public (TimeSpan.FromSeconds(float seconds))) vary None

0 comments on commit fa3a867

Please sign in to comment.