├── .gitattributes ├── samples └── GiraffeRazorSample │ ├── Views │ ├── Hello.cshtml │ ├── Partial.cshtml │ ├── _ViewStart.cshtml │ ├── Person.cshtml │ ├── _Layout.cshtml │ ├── FileUpload.cshtml │ └── CreatePerson.cshtml │ ├── WebRoot │ └── main.css │ ├── GiraffeRazorSample.fsproj │ └── Program.fs ├── giraffe-64x64.png ├── NuGet.config ├── src └── Giraffe.Razor │ ├── Middleware.fs │ ├── Giraffe.Razor.fsproj │ ├── HttpHandlers.fs │ └── RazorEngine.fs ├── RELEASE_NOTES.md ├── .github └── workflows │ └── build.yml ├── CODE_OF_CONDUCT.md ├── Giraffe.Razor.sln ├── README.md ├── .gitignore └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/Hello.cshtml: -------------------------------------------------------------------------------- 1 | Hello @ViewData["Who"], @ViewData["Name"] -------------------------------------------------------------------------------- /giraffe-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giraffe-fsharp/Giraffe.Razor/master/giraffe-64x64.png -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/Partial.cshtml: -------------------------------------------------------------------------------- 1 |

Greetings @ViewData["Title"]

2 |

Some partial text.

-------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Name"] = "from _ViewStart file"; 3 | } -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/Person.cshtml: -------------------------------------------------------------------------------- 1 | @model GiraffeRazorSample.Person 2 | @{ 3 | Layout = "_Layout"; 4 | } 5 | 6 |
7 |

Hello, @Model.Name

8 |
9 |
10 | @{ 11 | await Html.RenderPartialAsync("Partial"); 12 | } 13 |
-------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GiraffeRazorSample - A Razor sample application for Giraffe 5 | 6 | 7 | 8 | @RenderBody() 9 | 10 | -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/WebRoot/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | } 4 | 5 | h1, h2, h3, h4, h5, h6 { color: chocolate; } 6 | 7 | input[type=submit] { 8 | display: block; 9 | margin: 1em 0; 10 | padding: .5em 1em; 11 | background: #eee; 12 | color: #222; 13 | border: 1px solid #aaa; 14 | border-radius: 3px; 15 | font-size: 1.1em; 16 | cursor: pointer; 17 | } 18 | 19 | input[type=submit]:hover { 20 | background: #f1f1f1; 21 | } 22 | 23 | .danger-text { 24 | color: #F00; 25 | } -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/FileUpload.cshtml: -------------------------------------------------------------------------------- 1 | @model String 2 | @{ 3 | Layout = "_Layout"; 4 | } 5 | 6 |
7 |

GiraffeRazorSample @Model

8 |
9 |
10 |

Small File Upload

11 | 12 |
13 |

Upload one or more files using this form:

14 | 15 | 16 |
17 | 18 |

Large File Upload

19 | 20 |
21 |

Upload one or more files using this form:

22 | 23 | 24 |
25 |
-------------------------------------------------------------------------------- /samples/GiraffeRazorSample/GiraffeRazorSample.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | GiraffeRazorSample 6 | GiraffeRazorSample 7 | false 8 | $(MSBuildThisFileDirectory) 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Giraffe.Razor/Middleware.fs: -------------------------------------------------------------------------------- 1 | namespace Giraffe.Razor 2 | 3 | [] 4 | module Middleware = 5 | 6 | open Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 7 | open Microsoft.Extensions.DependencyInjection 8 | open Microsoft.Extensions.FileProviders 9 | 10 | type IServiceCollection with 11 | 12 | member this.AddRazorEngine(viewsFolderPath : string, persistFileProviders : bool) = 13 | this.Configure(fun (options: MvcRazorRuntimeCompilationOptions) -> 14 | if not persistFileProviders then 15 | options.FileProviders.Clear() 16 | else 17 | () 18 | options.FileProviders.Add(new PhysicalFileProvider(viewsFolderPath))).AddMvc().AddRazorRuntimeCompilation() 19 | |> ignore 20 | this.AddAntiforgery() 21 | 22 | member this.AddRazorEngine(viewsFolderPath : string) = 23 | this.AddRazorEngine(viewsFolderPath, false) 24 | 25 | member this.AddRazorEngine() = 26 | this.AddMvc().AddRazorRuntimeCompilation() |> ignore 27 | this.AddAntiforgery() 28 | -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Views/CreatePerson.cshtml: -------------------------------------------------------------------------------- 1 | @model GiraffeRazorSample.CreatePerson 2 | @{ 3 | Layout = "_Layout"; 4 | } 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | 7 |
8 |

Create Person

9 |
10 |
11 |

12 | 13 | 14 | 15 |

16 |

17 | 26 | 27 | 33 | 34 | 35 |

36 | 37 |
38 |
-------------------------------------------------------------------------------- /src/Giraffe.Razor/Giraffe.Razor.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Giraffe.Razor 5 | Razor view engine support for the Giraffe web framework. 6 | Copyright 2020 Dustin Moris Gorski 7 | Dustin Moris Gorski and contributors 8 | en-GB 9 | 10 | 11 | $(NoWarn);NU5104 12 | 13 | 14 | net5.0 15 | portable 16 | Library 17 | true 18 | false 19 | true 20 | true 21 | 22 | 23 | Giraffe.Razor 24 | Giraffe;Razor;ASP.NET Core;Lambda;FSharp;Functional;Http;Web;Framework;Micro;Service 25 | https://raw.githubusercontent.com/giraffe-fsharp/Giraffe.Razor/master/RELEASE_NOTES.md 26 | giraffe-64x64.png 27 | https://github.com/giraffe-fsharp/Giraffe.Razor 28 | Apache-2.0 29 | true 30 | git 31 | https://github.com/giraffe-fsharp/Giraffe.Razor 32 | 33 | 34 | true 35 | true 36 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 37 | $(NoWarn);FS2003 38 | 39 | 40 | 41 | 42 | true 43 | $(PackageIconUrl) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/Giraffe.Razor/HttpHandlers.fs: -------------------------------------------------------------------------------- 1 | namespace Giraffe.Razor 2 | 3 | [] 4 | module HttpHandlers = 5 | 6 | open System.Text 7 | open System.Threading.Tasks 8 | open System.Collections.Generic 9 | open Microsoft.AspNetCore.Http 10 | open Microsoft.AspNetCore.Mvc.ModelBinding 11 | open Microsoft.AspNetCore.Mvc.Razor 12 | open Microsoft.AspNetCore.Mvc.ViewFeatures 13 | open Microsoft.Extensions.DependencyInjection 14 | open Microsoft.AspNetCore.Antiforgery 15 | open FSharp.Control.Tasks 16 | open Giraffe 17 | open RazorEngine 18 | 19 | /// Reads a razor view from disk and compiles it with the given model and sets 20 | /// the compiled output as the HTTP reponse with the given contentType. 21 | let razorView (contentType : string) 22 | (viewName : string) 23 | (model : 'T option) 24 | (viewData : IDictionary option) 25 | (modelState : ModelStateDictionary option) : HttpHandler = 26 | fun (next : HttpFunc) (ctx : HttpContext) -> 27 | task { 28 | let metadataProvider = ctx.RequestServices.GetService() 29 | let engine = ctx.RequestServices.GetService() 30 | let tempDataDict = ctx.RequestServices.GetService().GetTempData ctx 31 | let! result = renderView engine metadataProvider tempDataDict ctx viewName model viewData modelState 32 | match result with 33 | | Error msg -> return (failwith msg) 34 | | Ok output -> 35 | let bytes = Encoding.UTF8.GetBytes output 36 | return! (setHttpHeader "Content-Type" contentType >=> setBody bytes) next ctx 37 | } 38 | 39 | /// Reads a razor view from disk and compiles it with the given model and sets 40 | /// the compiled output as the HTTP reponse with a Content-Type of text/html. 41 | let razorHtmlView (viewName : string) 42 | (model : 'T option) 43 | (viewData : IDictionary option) 44 | (modelState : ModelStateDictionary option) : HttpHandler = 45 | razorView "text/html; charset=utf-8" viewName model viewData modelState 46 | 47 | /// Validates an anti forgery token. 48 | /// If the token is valid the handler will procceed as normal, 49 | /// otherwise it will execute the invalidTokenHandler. 50 | let validateAntiforgeryToken (invalidTokenHandler : HttpHandler) : HttpHandler = 51 | fun next ctx -> 52 | task { 53 | let antiforgery = ctx.GetService() 54 | let! isValid = antiforgery.IsRequestValidAsync ctx 55 | return! 56 | if isValid then next ctx 57 | else invalidTokenHandler (Some >> Task.FromResult) ctx 58 | } -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ============= 3 | 4 | ## 5.1.0-rc-2 5 | 6 | - Updated `Giraffe` dependency to `5.0.0-rc-6` and changed from `TaskBuilder.fs` to `Ply` for `task` CEs. 7 | 8 | ## 5.1.0-rc-1 9 | 10 | - Upgraded to .NET 5 11 | - Added an overload to the `AddRazorEngine` extension method to set if existing file providers should be kept or cleared via the `persistFileProviders` flag. 12 | 13 | ## 5.0.0 14 | 15 | - Dropped support for all .NET framework monikers except .NET Core 3.1 (in preparation for the .NET 5 release) 16 | - Added support for `IModelMetadataProvider` 17 | 18 | ## 4.0.0 19 | 20 | #### Breaking changes & Bug fixes 21 | 22 | - Fixed a bug around the `TempData` implementation to ensure that all previously stored data in the `TempData` dictionary is accessible as well as newly made changes are persisted going forward. 23 | 24 | #### New features 25 | 26 | - Added an additional overload of `AddRazorEngine` which does not take any arguments, which supports Razor views from a class library. 27 | 28 | ## 3.0.0 29 | 30 | #### New features/Breaking changes 31 | 32 | - Added a new parameter to the `razorView`, `razorHtmlView`, and `RazorEngine.renderView` functions to support a `ModelStateDictionary`. 33 | 34 | ## 2.0.0 35 | 36 | #### New features/Breaking changes 37 | 38 | - Added support for view data in the `razorView` and `razorHtmlView` http handlers 39 | 40 | #### Improvements 41 | 42 | - Upgraded to Giraffe 3.2.* 43 | - Upgraded to latest TaskBuilder.fs 44 | - Added `charset=utf-8` to the `Content-Type` header when calling `razorHtmlView` 45 | 46 | ## 1.3.0 47 | 48 | Upgraded to Giraffe `2.0.0` and the latest ASP.NET Core `2.1.*` NuGet packages. 49 | 50 | ## 1.2.0 51 | 52 | Added support for view folders (see [#1](https://github.com/giraffe-fsharp/Giraffe.Razor/issues/1), [#3](https://github.com/giraffe-fsharp/Giraffe.Razor/issues/3)). 53 | 54 | ## 1.1.0 55 | 56 | Upgraded to Giraffe `1.1.0`. 57 | 58 | ## 1.0.1 59 | 60 | Changed copyright notice in NuGet package. 61 | 62 | ## 1.0.0 63 | 64 | Upgraded to latest Giraffe `1.0.0` release. 65 | 66 | ## 0.1.0-beta-310 67 | 68 | Upgraded to latest Giraffe `0.1.0-beta-6**` release. 69 | 70 | ## 0.1.0-beta-300 71 | 72 | Upgraded to latest Giraffe `0.1.0-beta-5**`, which has a new dependency on `Giraffe.Tasks` from NuGet. 73 | 74 | #### New features 75 | 76 | - Added `validateAntiforgeryToken` http handler for verifying anti forgery tokens. 77 | 78 | ## 0.1.0-beta-200 79 | 80 | #### Breaking changes 81 | 82 | - Renamed the `Giraffe.Razor.Engine` module to `RazorEngine` and moved it under the namespace `Giraffe.Razor`. If you were using the `renderRazorView` function to render individual razor views then you have to change the function call to `RazorEngine.renderView` now. 83 | 84 | (Most Giraffe users will probably not bet affected if they were using the `razorView` or `razorHtmlView` http handlers.) 85 | 86 | ## 0.1.0-beta-110 and before 87 | 88 | Previous releases of this library were documented in [Giraffe's release notes](https://github.com/giraffe-fsharp/Giraffe/blob/master/RELEASE_NOTES.md). -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | on: 3 | push: 4 | pull_request: 5 | release: 6 | types: 7 | - published 8 | env: 9 | # Stop wasting time caching packages 10 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 11 | # Disable sending usage data to Microsoft 12 | DOTNET_CLI_TELEMETRY_OPTOUT: true 13 | # Project name to pack and publish 14 | PROJECT_NAME: Giraffe.Razor 15 | # GitHub Packages Feed settings 16 | GITHUB_FEED: https://nuget.pkg.github.com/giraffe-fsharp/ 17 | GITHUB_USER: dustinmoris 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | # Official NuGet Feed settings 20 | NUGET_FEED: https://api.nuget.org/v3/index.json 21 | NUGET_KEY: ${{ secrets.NUGET_KEY }} 22 | jobs: 23 | build: 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | os: [ ubuntu-latest, windows-latest, macos-latest ] 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v2 31 | - name: Setup .NET Core 32 | uses: actions/setup-dotnet@v1 33 | with: 34 | dotnet-version: 5.0.100 35 | - name: Restore 36 | run: dotnet restore 37 | - name: Build 38 | run: dotnet build -c Release --no-restore 39 | - name: Test 40 | run: dotnet test -c Release 41 | - name: Pack 42 | if: matrix.os == 'ubuntu-latest' 43 | run: dotnet pack -v normal -c Release --no-restore --include-symbols --include-source -p:PackageVersion=$GITHUB_RUN_ID src/$PROJECT_NAME/$PROJECT_NAME.*proj 44 | - name: Upload Artifact 45 | if: matrix.os == 'ubuntu-latest' 46 | uses: actions/upload-artifact@v2 47 | with: 48 | name: nupkg 49 | path: ./src/${{ env.PROJECT_NAME }}/bin/Release/*.nupkg 50 | prerelease: 51 | needs: build 52 | if: github.ref == 'refs/heads/develop' 53 | runs-on: ubuntu-latest 54 | steps: 55 | - name: Download Artifact 56 | uses: actions/download-artifact@v1 57 | with: 58 | name: nupkg 59 | - name: Push to GitHub Feed 60 | run: | 61 | for f in ./nupkg/*.nupkg 62 | do 63 | curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED 64 | done 65 | deploy: 66 | needs: build 67 | if: github.event_name == 'release' 68 | runs-on: ubuntu-latest 69 | steps: 70 | - uses: actions/checkout@v2 71 | - name: Setup .NET Core 72 | uses: actions/setup-dotnet@v1 73 | with: 74 | dotnet-version: 5.0.100 75 | - name: Create Release NuGet package 76 | run: | 77 | arrTag=(${GITHUB_REF//\// }) 78 | VERSION="${arrTag[2]}" 79 | echo Version: $VERSION 80 | VERSION="${VERSION//v}" 81 | echo Clean Version: $VERSION 82 | dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/$PROJECT_NAME/$PROJECT_NAME.*proj 83 | - name: Push to GitHub Feed 84 | run: | 85 | for f in ./nupkg/*.nupkg 86 | do 87 | curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED 88 | done 89 | - name: Push to NuGet Feed 90 | run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at giraffe@dusted.codes. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /src/Giraffe.Razor/RazorEngine.fs: -------------------------------------------------------------------------------- 1 | namespace Giraffe.Razor 2 | 3 | module RazorEngine = 4 | 5 | open System 6 | open System.IO 7 | open System.Collections.Generic 8 | open Microsoft.AspNetCore.Http 9 | open Microsoft.AspNetCore.Mvc 10 | open Microsoft.AspNetCore.Mvc.Abstractions 11 | open Microsoft.AspNetCore.Mvc.ModelBinding 12 | open Microsoft.AspNetCore.Mvc.Razor 13 | open Microsoft.AspNetCore.Mvc.Rendering 14 | open Microsoft.AspNetCore.Mvc.ViewFeatures 15 | open Microsoft.AspNetCore.Routing 16 | open FSharp.Control.Tasks 17 | 18 | let private extractRouteData (path : string) = 19 | // Normalize nulls 20 | let templatePath = path + "" 21 | 22 | // Split path into segments and reverse the orders 23 | let segments = 24 | templatePath.Split('/', '\\') 25 | |> List.ofSeq 26 | |> List.rev 27 | 28 | let routeValues = 29 | seq { 30 | for i in 1..segments.Length do 31 | match i with 32 | | 1 -> yield "action", segments.[0] 33 | | 2 -> yield "controller", segments.[1] 34 | | 3 -> yield "area", segments.[2] 35 | | x -> yield sprintf "token-%d" (x), segments.[x - 1] 36 | } 37 | 38 | // Create RouteData Object using Values Created 39 | let routeData = RouteData() 40 | 41 | for (key,value) in routeValues do 42 | routeData.Values.Add(key, value) 43 | 44 | routeData 45 | 46 | let renderView (razorViewEngine : IRazorViewEngine) 47 | (modelMetadataProvider : IModelMetadataProvider) 48 | (tempDataDict : ITempDataDictionary) 49 | (httpContext : HttpContext) 50 | (viewName : string) 51 | (model : 'T option) 52 | (viewData : IDictionary option) 53 | (modelState : ModelStateDictionary option) = 54 | task { 55 | let routeData = extractRouteData(viewName) 56 | let templateName = routeData.Values.["action"].ToString() 57 | 58 | let actionContext = ActionContext(httpContext, routeData, ActionDescriptor()) 59 | let viewEngineResult = razorViewEngine.FindView(actionContext, templateName, true) 60 | 61 | match viewEngineResult.Success with 62 | | false -> 63 | let locations = String.Join(" ", viewEngineResult.SearchedLocations) 64 | return Error (sprintf "Could not find view with the name '%s'. Looked in %s." templateName locations) 65 | | true -> 66 | let view = viewEngineResult.View 67 | let viewModel = defaultArg model Unchecked.defaultof<'T> 68 | let viewModelState = defaultArg modelState (ModelStateDictionary()) 69 | let viewDataDict = 70 | ViewDataDictionary<'T>( 71 | modelMetadataProvider, 72 | viewModelState, 73 | Model = viewModel) 74 | if (viewData.IsSome) then 75 | viewData.Value 76 | |> Seq.iter (fun x -> viewDataDict.Add x) 77 | let htmlHelperOptions = HtmlHelperOptions() 78 | use output = new StringWriter() 79 | let viewContext = ViewContext(actionContext, view, viewDataDict, tempDataDict, output, htmlHelperOptions) 80 | do! view.RenderAsync(viewContext) 81 | tempDataDict.Save() 82 | return Ok (output.ToString()) 83 | } -------------------------------------------------------------------------------- /Giraffe.Razor.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{03CCBA43-B6F5-4FA7-8C18-432BB07225FC}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Giraffe.Razor", "src\Giraffe.Razor\Giraffe.Razor.fsproj", "{9B52367B-CC80-452E-9062-7583995BFAC0}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E067A6B8-538E-4362-83C2-9ECBC486AC31}" 11 | EndProject 12 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GiraffeRazorSample", "samples\GiraffeRazorSample\GiraffeRazorSample.fsproj", "{14818084-9969-4F42-8A66-394FC6670B50}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_root", "_root", "{73E09F74-F64D-4C42-92FC-19731C0C5553}" 15 | ProjectSection(SolutionItems) = preProject 16 | .gitignore = .gitignore 17 | .gitattributes = .gitattributes 18 | LICENSE = LICENSE 19 | NuGet.config = NuGet.config 20 | README.md = README.md 21 | RELEASE_NOTES.md = RELEASE_NOTES.md 22 | giraffe-64x64.png = giraffe-64x64.png 23 | CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md 24 | EndProjectSection 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Debug|x64 = Debug|x64 30 | Debug|x86 = Debug|x86 31 | Release|Any CPU = Release|Any CPU 32 | Release|x64 = Release|x64 33 | Release|x86 = Release|x86 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(NestedProjects) = preSolution 39 | {9B52367B-CC80-452E-9062-7583995BFAC0} = {03CCBA43-B6F5-4FA7-8C18-432BB07225FC} 40 | {14818084-9969-4F42-8A66-394FC6670B50} = {E067A6B8-538E-4362-83C2-9ECBC486AC31} 41 | EndGlobalSection 42 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 43 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Debug|x64.ActiveCfg = Debug|Any CPU 46 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Debug|x64.Build.0 = Debug|Any CPU 47 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Debug|x86.ActiveCfg = Debug|Any CPU 48 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Debug|x86.Build.0 = Debug|Any CPU 49 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Release|x64.ActiveCfg = Release|Any CPU 52 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Release|x64.Build.0 = Release|Any CPU 53 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Release|x86.ActiveCfg = Release|Any CPU 54 | {9B52367B-CC80-452E-9062-7583995BFAC0}.Release|x86.Build.0 = Release|Any CPU 55 | {14818084-9969-4F42-8A66-394FC6670B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {14818084-9969-4F42-8A66-394FC6670B50}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {14818084-9969-4F42-8A66-394FC6670B50}.Debug|x64.ActiveCfg = Debug|Any CPU 58 | {14818084-9969-4F42-8A66-394FC6670B50}.Debug|x64.Build.0 = Debug|Any CPU 59 | {14818084-9969-4F42-8A66-394FC6670B50}.Debug|x86.ActiveCfg = Debug|Any CPU 60 | {14818084-9969-4F42-8A66-394FC6670B50}.Debug|x86.Build.0 = Debug|Any CPU 61 | {14818084-9969-4F42-8A66-394FC6670B50}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {14818084-9969-4F42-8A66-394FC6670B50}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {14818084-9969-4F42-8A66-394FC6670B50}.Release|x64.ActiveCfg = Release|Any CPU 64 | {14818084-9969-4F42-8A66-394FC6670B50}.Release|x64.Build.0 = Release|Any CPU 65 | {14818084-9969-4F42-8A66-394FC6670B50}.Release|x86.ActiveCfg = Release|Any CPU 66 | {14818084-9969-4F42-8A66-394FC6670B50}.Release|x86.Build.0 = Release|Any CPU 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Giraffe](https://raw.githubusercontent.com/giraffe-fsharp/Giraffe/master/giraffe.png) 2 | 3 | # Giraffe.Razor 4 | 5 | Razor view engine support for the [Giraffe](https://github.com/giraffe-fsharp/Giraffe) web framework. 6 | 7 | [![NuGet Info](https://buildstats.info/nuget/Giraffe.Razor?includePreReleases=true)](https://www.nuget.org/packages/Giraffe.Razor/) 8 | 9 | ### Linux, macOS and Windows Build Status 10 | 11 | ![.NET Core](https://github.com/giraffe-fsharp/Giraffe.Razor/workflows/.NET%20Core/badge.svg?branch=develop) 12 | 13 | [![Build history](https://buildstats.info/github/chart/giraffe-fsharp/Giraffe.Razor?branch=develop&includeBuildsFromPullRequest=false)](https://github.com/giraffe-fsharp/Giraffe.Razor/actions?query=branch%3Adevelop++) 14 | 15 | ## Table of contents 16 | 17 | - [Documentation](#documentation) 18 | - [razorView](#razorview) 19 | - [razorHtmlView](#razorhtmlview) 20 | - [validateAntiforgeryToken](#validateantiforgerytoken) 21 | - [Samples](#samples) 22 | - [Nightly builds and NuGet feed](#nightly-builds-and-nuget-feed) 23 | - [More information](#more-information) 24 | - [License](#license) 25 | 26 | ## Documentation 27 | 28 | The `Giraffe.Razor` NuGet package adds additional `HttpHandler` functions to render Razor views in Giraffe. 29 | 30 | In order to use the Razor functionality in an (ASP.NET Core) Giraffe application you'll need to register additional dependencies through the `AddRazorEngine` extension method during application start-up: 31 | 32 | ```fsharp 33 | open Giraffe 34 | open Giraffe.Razor 35 | 36 | type Startup() = 37 | member __.ConfigureServices (svc : IServiceCollection, 38 | env : IHostingEnvironment) = 39 | let viewsFolderPath = 40 | Path.Combine(env.ContentRootPath, "Views") 41 | svc.AddRazorEngine viewsFolderPath |> ignore 42 | ``` 43 | 44 | If your all of your Razor views are kept in a Razor class library, then you do not need to specify a views folder path when registering the Razor dependencies. In this case there is an overload of `AddRazorEngine` which takes no arguments: 45 | 46 | ```fsharp 47 | open Giraffe 48 | open Giraffe.Razor 49 | 50 | type Startup() = 51 | member __.ConfigureServices (svc : IServiceCollection, 52 | env : IHostingEnvironment) = 53 | svc.AddRazorEngine() |> ignore 54 | ``` 55 | 56 | ### razorView 57 | 58 | The `razorView` http handler utilises the official ASP.NET Core MVC Razor view engine to compile a view into a HTML page and sets the body of the `HttpResponse` object. It requires the content type, the view name, an optional view model, an optional view data dictionary, and an optional model state dictionary as input parameters. 59 | 60 | #### Example: 61 | 62 | Use the razorView function: 63 | 64 | ```fsharp 65 | open Giraffe 66 | open Giraffe.Razor 67 | 68 | [] 69 | type TestModel = 70 | { 71 | WelcomeText : string 72 | } 73 | 74 | let model = { WelcomeText = "Hello, World" } 75 | 76 | let app = 77 | choose [ 78 | // Assuming there is a view called "Index.cshtml" 79 |        route "/" >=> razorView "text/html" "Index" (Some model) None None 80 | ] 81 | ``` 82 | 83 | ### razorHtmlView 84 | 85 | The `razorHtmlView` http handler is the same as the `razorView` handler except that it automatically sets the response's content type to `text/html; charset=utf-8`. 86 | 87 | #### Example: 88 | 89 | Use the razorView function: 90 | 91 | ```fsharp 92 | open Giraffe 93 | open Giraffe.Razor 94 | open Microsoft.AspNetCore.Mvc.ModelBinding 95 | 96 | [] 97 | type TestModel = 98 | { 99 | WelcomeText : string 100 | } 101 | 102 | let model = { WelcomeText = "Hello, World" } 103 | 104 | let viewData = 105 | dict [ 106 | "Title", "Hello World" :> obj 107 | "Foo", 89 :> obj 108 | "Bar", true :> obj 109 | ] 110 | 111 | let modelState = ModelStateDictionary() 112 | 113 | let app = 114 | choose [ 115 | // Assuming there is a view called "Index.cshtml" 116 |        route "/" >=> razorHtmlView "Index" (Some model) (Some viewData) (Some modelState) 117 | ] 118 | ``` 119 | 120 | ### validateAntiforgeryToken 121 | 122 | The `validateAntiforgeryToken` http handler allows one to validate an anti forgery token created by the `Microsoft.AspNetCore.Antiforgery` API. 123 | 124 | #### Example: 125 | 126 | Inside a razor view add an anti forgery token: 127 | 128 | ```html 129 |
130 | @Html.AntiforgeryToken 131 | 132 |
133 | ``` 134 | 135 | Use the `validateAntiforgeryToken` function to validate the form request: 136 | 137 | ```fsharp 138 | open Giraffe 139 | open Giraffe.Razor 140 | 141 | let invalidCSRFTokenHandler = RequestErrors.badRequest "The CSRF token was invalid" 142 | 143 | let app = 144 | POST 145 | >=> route "/submit-sth" 146 | >=> validateAntiforgeryToken invalidCSRFTokenHandler 147 | >=> Successful.OK "All good" 148 | ``` 149 | 150 | ## Samples 151 | 152 | Please find a fully functioning sample application under [./samples/GiraffeRazorSample/](https://github.com/giraffe-fsharp/Giraffe.Razor/tree/master/samples/GiraffeRazorSample). 153 | 154 | ## Nightly builds and NuGet feed 155 | 156 | All official release packages are published to the official and public NuGet feed. 157 | 158 | Nightly builds (builds from the `develop` branch) produce unofficial pre-release packages which can be pulled from the [project's NuGet feed on GitHub](https://github.com/orgs/giraffe-fsharp/packages). 159 | 160 | These packages are being tagged with the Workflow's run number as the package version. 161 | 162 | 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. 163 | 164 | ## More information 165 | 166 | For more information about Giraffe, how to set up a development environment, contribution guidelines and more please visit the [main documentation](https://github.com/giraffe-fsharp/Giraffe/blob/master/DOCUMENTATION.md) page. 167 | 168 | ## License 169 | 170 | [Apache 2.0](https://raw.githubusercontent.com/giraffe-fsharp/Giraffe.Razor/master/LICENSE) -------------------------------------------------------------------------------- /samples/GiraffeRazorSample/Program.fs: -------------------------------------------------------------------------------- 1 | module GiraffeRazorSample 2 | 3 | open System 4 | open System.IO 5 | open System.Threading 6 | open Microsoft.AspNetCore 7 | open Microsoft.AspNetCore.Builder 8 | open Microsoft.AspNetCore.Hosting 9 | open Microsoft.AspNetCore.Http 10 | open Microsoft.AspNetCore.Http.Features 11 | open Microsoft.AspNetCore.Mvc.ModelBinding 12 | open Microsoft.Extensions.Logging 13 | open Microsoft.Extensions.DependencyInjection 14 | open FSharp.Control.Tasks 15 | open Giraffe 16 | open Giraffe.Razor 17 | 18 | // --------------------------------- 19 | // Models 20 | // --------------------------------- 21 | 22 | [] 23 | type Person = 24 | { 25 | Name : string 26 | } 27 | 28 | [] 29 | type CreatePerson = 30 | { 31 | Name : string 32 | CheckMe : bool 33 | } 34 | 35 | // --------------------------------- 36 | // Web app 37 | // --------------------------------- 38 | 39 | let antiforgeryTokenHandler = 40 | text "Bad antiforgery token" 41 | |> RequestErrors.badRequest 42 | |> validateAntiforgeryToken 43 | 44 | let bytesToKbStr (bytes : int64) = 45 | sprintf "%ikb" (bytes / 1024L) 46 | 47 | let displayFileInfos (files : IFormFileCollection) = 48 | files 49 | |> Seq.fold (fun acc file -> 50 | sprintf "%s\n\n%s\n%s" acc file.FileName (bytesToKbStr file.Length)) "" 51 | |> text 52 | 53 | let smallFileUploadHandler = 54 | fun (next : HttpFunc) (ctx : HttpContext) -> 55 | task { 56 | return! 57 | (match ctx.Request.HasFormContentType with 58 | | false -> text "Bad request" |> RequestErrors.badRequest 59 | | true -> ctx.Request.Form.Files |> displayFileInfos) next ctx 60 | } 61 | 62 | let largeFileUploadHandler = 63 | fun (next : HttpFunc) (ctx : HttpContext) -> 64 | task { 65 | let formFeature = ctx.Features.Get() 66 | let! form = formFeature.ReadFormAsync CancellationToken.None 67 | return! (form.Files |> displayFileInfos) next ctx 68 | } 69 | 70 | let renderPerson = 71 | fun (next : HttpFunc) (ctx : HttpContext) -> 72 | task { 73 | let name = 74 | match ctx.TryGetQueryStringValue "name" with 75 | | Some n -> n 76 | | None -> "Razor" 77 | let model = { Name = name } 78 | let viewData = dict [("Title", box "Mr Fox")] 79 | return! razorHtmlView "Person" (Some model) (Some viewData) None next ctx 80 | } 81 | 82 | let renderCreatePerson = 83 | let model = { Name = ""; CheckMe = true } 84 | let viewData = dict [("Title", box "Create peson")] 85 | razorHtmlView "CreatePerson" (Some model) (Some viewData) None 86 | 87 | let createPerson = 88 | fun (next : HttpFunc) (ctx : HttpContext) -> 89 | task { 90 | let modelState = ModelStateDictionary() 91 | let! model = ctx.BindModelAsync() 92 | if not model.CheckMe then 93 | modelState.AddModelError("CheckMe", "Checkbox must be checked") 94 | modelState.AddModelError(String.Empty, "Error without an associated field") 95 | if String.IsNullOrWhiteSpace(model.Name) then modelState.AddModelError("Name", "Name is rquired") 96 | if modelState.IsValid then 97 | let url = sprintf "/person?name=%s" model.Name 98 | return! redirectTo false url next ctx 99 | else 100 | let viewData = dict [("Title", box "Create peson")] 101 | return! razorHtmlView "CreatePerson" (Some model) (Some viewData) (Some modelState) next ctx 102 | } 103 | 104 | let viewData = 105 | dict [ 106 | "Who", "Foo Bar" :> obj 107 | "Foo", 89 :> obj 108 | "Bar", true :> obj 109 | ] 110 | 111 | let webApp = 112 | choose [ 113 | GET >=> 114 | choose [ 115 | route "/" >=> text "index" 116 | route "/razor" >=> razorView "text/html" "Hello" None (Some viewData) None 117 | route "/person/create" >=> renderCreatePerson 118 | route "/person" >=> renderPerson 119 | route "/upload" >=> razorHtmlView "FileUpload" (Some "File upload") (Some viewData) None 120 | ] 121 | POST >=> 122 | choose [ 123 | route "/small-upload" >=> smallFileUploadHandler 124 | route "/large-upload" >=> largeFileUploadHandler 125 | route "/person/create" >=> antiforgeryTokenHandler >=> createPerson 126 | ] 127 | text "Not Found" |> RequestErrors.notFound ] 128 | 129 | // --------------------------------- 130 | // Error handler 131 | // --------------------------------- 132 | 133 | let errorHandler (ex : Exception) (logger : ILogger) = 134 | logger.LogError(EventId(), ex, "An unhandled exception has occurred while executing the request.") 135 | clearResponse >=> ServerErrors.INTERNAL_ERROR (text ex.Message) 136 | 137 | // --------------------------------- 138 | // Main 139 | // --------------------------------- 140 | 141 | let configureApp (app : IApplicationBuilder) = 142 | app.UseGiraffeErrorHandler(errorHandler) 143 | .UseStaticFiles() 144 | .UseAuthentication() 145 | .UseGiraffe webApp 146 | 147 | let configureServices (services : IServiceCollection) = 148 | let sp = services.BuildServiceProvider() 149 | let env = sp.GetService() 150 | services.AddGiraffe() |> ignore 151 | Path.Combine(env.ContentRootPath, "Views") 152 | |> services.AddRazorEngine 153 | |> ignore 154 | 155 | let configureLogging (loggerBuilder : ILoggingBuilder) = 156 | loggerBuilder.AddFilter(fun lvl -> lvl.Equals LogLevel.Error) 157 | .AddConsole() 158 | .AddDebug() |> ignore 159 | 160 | [] 161 | let main _ = 162 | let contentRoot = Directory.GetCurrentDirectory() 163 | let webRoot = Path.Combine(contentRoot, "WebRoot") 164 | WebHost.CreateDefaultBuilder() 165 | .UseWebRoot(webRoot) 166 | .Configure(Action configureApp) 167 | .ConfigureServices(configureServices) 168 | .ConfigureLogging(configureLogging) 169 | .Build() 170 | .Run() 171 | 0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | 332 | # macOS 333 | .DS_Store 334 | 335 | # Ionide 336 | .ionide/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------