Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MapAction sample #292

Merged
merged 13 commits into from
Nov 20, 2021
11 changes: 5 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,22 @@ jobs:

steps:
- uses: actions/checkout@v2
- name: Setup dotnet 2.1
- name: Setup .NET 2.1
uses: actions/setup-dotnet@v1
with:
dotnet-version: 2.1.*
- name: Setup dotnet 3.1
- name: Setup .NET 3.1
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.*
- name: Setup dotnet 5.0
- name: Setup .NET 5.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.*
- name: Setup dotnet 6.0
- name: Setup .NET 6.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
include-prerelease: true
dotnet-version: 6.0.*
- name: Test
run: dotnet test --configuration Release
- name: Test - Compiled
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ jobs:
- name: Setup dotnet 6.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
include-prerelease: true
dotnet-version: 6.0.*
- name: Install dependencies
run: dotnet restore
- name: Test
Expand Down
10 changes: 9 additions & 1 deletion Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@
<DebugSymbols>true</DebugSymbols>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<DebugType>portable</DebugType>
<IsPackable>true</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<NoWarn>$(NoWarn);1591</NoWarn>

<!-- Sourcelink -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
Expand Down
16 changes: 16 additions & 0 deletions Fluid.MinimalApisSample/Fluid.MinimalApisSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Exe</OutputType>
<NoWarn>$(NoWarn);NU5104</NoWarn>
<IsPackable>false</IsPackable>
<LangVersion>preview</LangVersion>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\MinimalApis.LiquidViews\MinimalApis.LiquidViews.csproj" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions Fluid.MinimalApisSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddFluid();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.MapGet("/", () => Results.Extensions.View("Index", new Todo(1, "Go back to work!", false)));

await app.RunAsync();

record Todo(int Id, string Name, bool IsComplete);
101 changes: 101 additions & 0 deletions Fluid.MinimalApisSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
## MinimalApis.LiquidViews

This library provides some extensions to ASP.NET Minimal APIs that allow to return templated view results using the Liquid language.
Liquid is fast and safe. Views are interpreted so changes are reflected very quickly without a compilation phase.

This View Engine is based on [Fluid](https://github.com/sebastienros/fluid), a Liquid template engine for .NET.

## Sample usage

These files demonstrates how to return a view result, which will be able to use
- a `_layout` file to act as a template for multiple pages
- a `_viewstart` file to be executed for each view
- a `component` file to act like partial views

The full sample can be found [here](https://github.com/sebastienros/fluid/tree/main/Fluid.MinimalApisSample)

#### Program.cs

```c#
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddFluid();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.MapGet("/", () => Results.Extensions.View("Index", new Todo(1, "Go back to work!", false)));

await app.RunAsync();

record Todo(int Id, string Name, bool IsComplete);
```

#### index.liquid

```liquid
<hr />
Hello World from the body

Name: {{ Name }} <br />
IsComplete: {{ IsComplete}}
<hr />

{% section footer %}
Hello from the footer section
{% endsection %}
```

#### _viewstart.liquid

```liquid
{% layout '_Layout' %}
From /Views/_ViewStart.liquid
```

#### component.liquid

```liquid
<div>Using a component {{ x }} + {{ y }} = {{ x | plus: y }}</div>
```

#### _layout.liquid

```liquid
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">

<title>Hello, world!</title>
</head>
<body>

<div class="px-4 py-5 my-5 text-center">
<h1 class="display-5 fw-bold">Title from the layout</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">
{% renderbody %}

{% partial 'Component', x: 1, y:2 %}

</p>
</div>
</div>

<pre>
{% rendersection footer %}
</pre>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>
</body>
</html>
```
33 changes: 33 additions & 0 deletions Fluid.MinimalApisSample/Views/_layout.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">

<title>Hello, world!</title>
</head>
<body>

<div class="px-4 py-5 my-5 text-center">
<h1 class="display-5 fw-bold">Title from the layout</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">
{% renderbody %}

{% partial 'Component', x: 1, y:2 %}

</p>
</div>
</div>

<pre>
{% rendersection footer %}
</pre>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions Fluid.MinimalApisSample/Views/_viewstart.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% layout '_Layout' %}
From /Views/_ViewStart.liquid
1 change: 1 addition & 0 deletions Fluid.MinimalApisSample/Views/component.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div>Using a component {{ x }} + {{ y }} = {{ x | plus: y }}</div>
10 changes: 10 additions & 0 deletions Fluid.MinimalApisSample/Views/index.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<hr />
Hello World from the body

Name: {{ Name }} <br />
IsComplete: {{ IsComplete}}
<hr />

{% section footer %}
Hello from the footer section
{% endsection %}
8 changes: 8 additions & 0 deletions Fluid.MinimalApisSample/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
2 changes: 1 addition & 1 deletion Fluid.MvcViewEngine/MvcViewOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class MvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
private readonly IFluidViewEngine _fluidViewEngine;

/// <summary>
/// Initializes a new instance of <see cref="FluidMvcViewOptionsSetup"/>.
/// Initializes a new instance of <see cref="MvcViewOptionsSetup"/>.
/// </summary>
/// <param name="fluidViewEngine">The <see cref="IFluidViewEngine"/>.</param>
public MvcViewOptionsSetup(IFluidViewEngine fluidViewEngine)
Expand Down
16 changes: 14 additions & 2 deletions Fluid.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28606.126
# Visual Studio Version 17
VisualStudioVersion = 17.1.31903.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluid", "Fluid\Fluid.csproj", "{8CD687F1-AC65-4E4A-A510-67768999C81C}"
EndProject
Expand All @@ -25,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{BE5EF08E
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluid.ViewEngine", "Fluid.ViewEngine\Fluid.ViewEngine.csproj", "{3510D4C5-8328-4425-B2F1-B8591E85B965}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalApis.LiquidViews", "MinimalApis.LiquidViews\MinimalApis.LiquidViews.csproj", "{DD0B5036-D5E6-4E6E-8405-27F6C2FD5917}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluid.MinimalApisSample", "Fluid.MinimalApisSample\Fluid.MinimalApisSample.csproj", "{6390F2D4-564B-455E-9C02-3DB998E5BD09}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -55,6 +59,14 @@ Global
{3510D4C5-8328-4425-B2F1-B8591E85B965}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3510D4C5-8328-4425-B2F1-B8591E85B965}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3510D4C5-8328-4425-B2F1-B8591E85B965}.Release|Any CPU.Build.0 = Release|Any CPU
{DD0B5036-D5E6-4E6E-8405-27F6C2FD5917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD0B5036-D5E6-4E6E-8405-27F6C2FD5917}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD0B5036-D5E6-4E6E-8405-27F6C2FD5917}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD0B5036-D5E6-4E6E-8405-27F6C2FD5917}.Release|Any CPU.Build.0 = Release|Any CPU
{6390F2D4-564B-455E-9C02-3DB998E5BD09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6390F2D4-564B-455E-9C02-3DB998E5BD09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6390F2D4-564B-455E-9C02-3DB998E5BD09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6390F2D4-564B-455E-9C02-3DB998E5BD09}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
8 changes: 7 additions & 1 deletion Fluid/Fluid.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageReference Include="Parlot" Version="0.0.17" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="1.1.1" />
<PackageReference Include="TimeZoneConverter" Version="3.5.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="all" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
Expand All @@ -36,4 +36,10 @@
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
</ItemGroup>

<PropertyGroup>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>
73 changes: 73 additions & 0 deletions MinimalApis.LiquidViews/ActionViewResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using Fluid;
using Fluid.ViewEngine;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Threading.Tasks;

namespace MinimalApis.LiquidViews
{
public class ActionViewResult : IResult
{
private readonly string _viewName;
private readonly object _model;

public ActionViewResult(string viewName)
{
_viewName = viewName;
_model = new object();
}

public ActionViewResult(string viewName, object model)
{
_viewName = viewName;
_model = model;
}

public string ContentType { get; set; } = "text/html";

public async Task ExecuteAsync(HttpContext httpContext)
{
var fluidViewRenderer = httpContext.RequestServices.GetService<IFluidViewRenderer>();
var options = httpContext.RequestServices.GetService<IOptions<FluidViewEngineOptions>>().Value;

var viewPath = LocatePageFromViewLocations(_viewName, options);

if (viewPath == null)
{
httpContext.Response.StatusCode = 404;
return;
}

httpContext.Response.StatusCode = 200;
httpContext.Response.ContentType = ContentType;

var context = new TemplateContext(_model);
context.Options.FileProvider = options.PartialsFileProvider;

await using var sw = new StreamWriter(httpContext.Response.Body);
await fluidViewRenderer.RenderViewAsync(sw, viewPath, context);
}

private static string LocatePageFromViewLocations(string viewName, FluidViewEngineOptions options)
{
var fileProvider = options.ViewsFileProvider;

foreach (var location in options.ViewsLocationFormats)
{
var viewFilename = Path.Combine(String.Format(location, viewName));

var fileInfo = fileProvider.GetFileInfo(viewFilename);

if (fileInfo.Exists)
{
return viewFilename;
}
}

return null;
}
}
}
Loading