Skip to content

Commit cbdaa22

Browse files
authored
Merge pull request #104 from andrewlock/add-yaml-formatters
Add Yaml input and output formatters
2 parents c15942a + a8ab68e commit cbdaa22

12 files changed

+616
-1
lines changed

WebApiContrib.Core.sln

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Microsoft Visual Studio Solution File, Format Version 12.00
22
# Visual Studio 15
3-
VisualStudioVersion = 15.0.26430.4
3+
VisualStudioVersion = 15.0.26430.6
44
MinimumVisualStudioVersion = 10.0.40219.1
55
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. Core", "01. Core", "{5898776F-DBF8-4C30-85A3-66401B12016F}"
66
EndProject
@@ -60,6 +60,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Formatte
6060
EndProject
6161
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Protobuf.Tests", "tests\WebApiContrib.Core.Protobuf.Tests\WebApiContrib.Core.Protobuf.Tests.csproj", "{DA5532DB-81C4-4F39-B6F2-164ECA46292B}"
6262
EndProject
63+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Formatter.Yaml", "src\WebApiContrib.Core.Formatter.Yaml\WebApiContrib.Core.Formatter.Yaml.csproj", "{D542971E-D9D3-4E8F-A4BF-D1638D7C2613}"
64+
EndProject
65+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiContrib.Core.Yaml.Tests", "tests\WebApiContrib.Core.Yaml.Tests\WebApiContrib.Core.Yaml.Tests.csproj", "{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB}"
66+
EndProject
6367
Global
6468
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6569
Debug|Any CPU = Debug|Any CPU
@@ -158,6 +162,14 @@ Global
158162
{DA5532DB-81C4-4F39-B6F2-164ECA46292B}.Debug|Any CPU.Build.0 = Debug|Any CPU
159163
{DA5532DB-81C4-4F39-B6F2-164ECA46292B}.Release|Any CPU.ActiveCfg = Release|Any CPU
160164
{DA5532DB-81C4-4F39-B6F2-164ECA46292B}.Release|Any CPU.Build.0 = Release|Any CPU
165+
{D542971E-D9D3-4E8F-A4BF-D1638D7C2613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
166+
{D542971E-D9D3-4E8F-A4BF-D1638D7C2613}.Debug|Any CPU.Build.0 = Debug|Any CPU
167+
{D542971E-D9D3-4E8F-A4BF-D1638D7C2613}.Release|Any CPU.ActiveCfg = Release|Any CPU
168+
{D542971E-D9D3-4E8F-A4BF-D1638D7C2613}.Release|Any CPU.Build.0 = Release|Any CPU
169+
{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
170+
{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
171+
{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
172+
{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB}.Release|Any CPU.Build.0 = Release|Any CPU
161173
EndGlobalSection
162174
GlobalSection(SolutionProperties) = preSolution
163175
HideSolutionNode = FALSE
@@ -188,5 +200,7 @@ Global
188200
{A93815F3-F713-4648-8AE3-A6676F66523A} = {A7A8D928-30D6-4497-8B7D-F233B79A2917}
189201
{2D6C001F-2154-4385-A112-DE2B1AC23220} = {5898776F-DBF8-4C30-85A3-66401B12016F}
190202
{DA5532DB-81C4-4F39-B6F2-164ECA46292B} = {A7A8D928-30D6-4497-8B7D-F233B79A2917}
203+
{D542971E-D9D3-4E8F-A4BF-D1638D7C2613} = {5898776F-DBF8-4C30-85A3-66401B12016F}
204+
{3EC99E77-9F1E-4D43-B70B-E67C61D7E8DB} = {A7A8D928-30D6-4497-8B7D-F233B79A2917}
191205
EndGlobalSection
192206
EndGlobal

appveyor.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ test_script:
1414
- cd tests/WebApiContrib.Core.Concurrency.Tests
1515
- dotnet test
1616
- cd ../../tests/WebApiContrib.Core.Protobuf.Tests
17+
- dotnet test
18+
- cd ../../tests/WebApiContrib.Core.Yaml.Tests
1719
- dotnet test
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>ASP.NET Core InputFormatter, OutputFormatter for Yaml</Description>
5+
<VersionPrefix>1.0.0</VersionPrefix>
6+
<Authors>andrewlock;WebApiContrib Contributors</Authors>
7+
<TargetFrameworks>netstandard1.6;net451</TargetFrameworks>
8+
<AssemblyName>WebApiContrib.Core.Formatter.Yaml</AssemblyName>
9+
<PackageId>WebApiContrib.Core.Formatter.Yaml</PackageId>
10+
<PackageTags>ASP.NET Core;InputFormatter;OutputFormatter;MediaFormatter;Yaml;Content-Type;aspnetcore</PackageTags>
11+
<PackageIconUrl>https://avatars1.githubusercontent.com/u/1561645?s=140</PackageIconUrl>
12+
<PackageProjectUrl>https://github.com/WebAPIContrib/WebAPIContrib.Core/tree/master/src/WebApiContrib.Core.Formatter.Yaml</PackageProjectUrl>
13+
<RepositoryType>git</RepositoryType>
14+
<RepositoryUrl>https://github.com/WebApiContrib/WebAPIContrib.Core.git</RepositoryUrl>
15+
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.6' ">$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
16+
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
17+
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
18+
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
19+
</PropertyGroup>
20+
21+
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
22+
<Reference Include="System.Runtime.Serialization" />
23+
<Reference Include="System.Runtime" />
24+
<Reference Include="System" />
25+
<Reference Include="Microsoft.CSharp" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.0.3" />
30+
<PackageReference Include="YamlDotNet" Version="4.2.1" />
31+
</ItemGroup>
32+
33+
</Project>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using System;
3+
using Microsoft.Net.Http.Headers;
4+
5+
namespace WebApiContrib.Core.Formatter.Yaml
6+
{
7+
public static class YamlFormatterMvcBuilderExtensions
8+
{
9+
public static IMvcBuilder AddYamlFormatters(this IMvcBuilder builder)
10+
{
11+
if (builder == null)
12+
{
13+
throw new ArgumentNullException(nameof(builder));
14+
}
15+
16+
return AddYamlFormatters(builder, YamlFormatterOptionsConfiguration: null);
17+
}
18+
19+
public static IMvcCoreBuilder AddYamlFormatters(this IMvcCoreBuilder builder)
20+
{
21+
if (builder == null)
22+
{
23+
throw new ArgumentNullException(nameof(builder));
24+
}
25+
26+
return AddYamlFormatters(builder, YamlFormatterOptionsConfiguration: null);
27+
}
28+
29+
public static IMvcBuilder AddYamlFormatters(this IMvcBuilder builder, Action<YamlFormatterOptions> YamlFormatterOptionsConfiguration)
30+
{
31+
if (builder == null)
32+
{
33+
throw new ArgumentNullException(nameof(builder));
34+
}
35+
36+
var YamlFormatterOptions = new YamlFormatterOptions();
37+
YamlFormatterOptionsConfiguration?.Invoke(YamlFormatterOptions);
38+
39+
foreach (var extension in YamlFormatterOptions.SupportedExtensions)
40+
{
41+
foreach (var contentType in YamlFormatterOptions.SupportedContentTypes)
42+
{
43+
builder.AddFormatterMappings(m => m.SetMediaTypeMappingForFormat(extension, new MediaTypeHeaderValue(contentType)));
44+
}
45+
}
46+
47+
builder.AddMvcOptions(options => options.InputFormatters.Add(new YamlInputFormatter(YamlFormatterOptions)));
48+
builder.AddMvcOptions(options => options.OutputFormatters.Add(new YamlOutputFormatter(YamlFormatterOptions)));
49+
50+
return builder;
51+
}
52+
53+
public static IMvcCoreBuilder AddYamlFormatters(this IMvcCoreBuilder builder, Action<YamlFormatterOptions> YamlFormatterOptionsConfiguration)
54+
{
55+
if (builder == null)
56+
{
57+
throw new ArgumentNullException(nameof(builder));
58+
}
59+
60+
var YamlFormatterOptions = new YamlFormatterOptions();
61+
YamlFormatterOptionsConfiguration?.Invoke(YamlFormatterOptions);
62+
63+
foreach (var extension in YamlFormatterOptions.SupportedExtensions)
64+
{
65+
foreach (var contentType in YamlFormatterOptions.SupportedContentTypes)
66+
{
67+
builder.AddFormatterMappings(m => m.SetMediaTypeMappingForFormat(extension, new MediaTypeHeaderValue(contentType)));
68+
}
69+
}
70+
71+
builder.AddMvcOptions(options => options.InputFormatters.Add(new YamlInputFormatter(YamlFormatterOptions)));
72+
builder.AddMvcOptions(options => options.OutputFormatters.Add(new YamlOutputFormatter(YamlFormatterOptions)));
73+
74+
return builder;
75+
}
76+
}
77+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Collections.Generic;
2+
using YamlDotNet.Serialization;
3+
4+
namespace WebApiContrib.Core.Formatter.Yaml
5+
{
6+
public class YamlFormatterOptions
7+
{
8+
public HashSet<string> SupportedContentTypes { get; set; } = new HashSet<string> { "application/x-yaml", "application/yaml", "text/x-yaml", "text/yaml" };
9+
public HashSet<string> SupportedExtensions { get; set; } = new HashSet<string> { "yaml" };
10+
}
11+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Mvc.Formatters;
5+
using Microsoft.Net.Http.Headers;
6+
using YamlDotNet.Serialization;
7+
8+
namespace WebApiContrib.Core.Formatter.Yaml
9+
{
10+
public class YamlInputFormatter : InputFormatter
11+
{
12+
private readonly YamlFormatterOptions _options;
13+
private readonly Deserializer _deserializer;
14+
15+
public YamlInputFormatter(YamlFormatterOptions YamlFormatterOptions)
16+
{
17+
_options = YamlFormatterOptions ?? throw new ArgumentNullException(nameof(YamlFormatterOptions));
18+
foreach (var contentType in YamlFormatterOptions.SupportedContentTypes)
19+
{
20+
SupportedMediaTypes.Add(new MediaTypeHeaderValue(contentType));
21+
}
22+
_deserializer = new Deserializer();
23+
}
24+
25+
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
26+
{
27+
var type = context.ModelType;
28+
using (var streamReader = new StreamReader(context.HttpContext.Request.Body))
29+
{
30+
object result = _deserializer.Deserialize(streamReader, type);
31+
return InputFormatterResult.SuccessAsync(result);
32+
}
33+
}
34+
35+
public override bool CanRead(InputFormatterContext context)
36+
{
37+
return true;
38+
}
39+
}
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Mvc.Formatters;
5+
using Microsoft.Net.Http.Headers;
6+
using YamlDotNet.Serialization;
7+
8+
namespace WebApiContrib.Core.Formatter.Yaml
9+
{
10+
public class YamlOutputFormatter : OutputFormatter
11+
{
12+
private readonly YamlFormatterOptions _options;
13+
private readonly Serializer _serializer;
14+
15+
public YamlOutputFormatter(YamlFormatterOptions YamlFormatterOptions)
16+
{
17+
ContentType = "application/x-yaml";
18+
_options = YamlFormatterOptions ?? throw new ArgumentNullException(nameof(YamlFormatterOptions));
19+
foreach (var contentType in YamlFormatterOptions.SupportedContentTypes)
20+
{
21+
SupportedMediaTypes.Add(new MediaTypeHeaderValue(contentType));
22+
}
23+
_serializer = new Serializer();
24+
}
25+
26+
public string ContentType { get; private set; }
27+
28+
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context)
29+
{
30+
var response = context.HttpContext.Response;
31+
32+
using (var streamWriter = new StreamWriter(response.Body))
33+
{
34+
_serializer.Serialize(streamWriter, context.Object);
35+
return Task.FromResult(response);
36+
}
37+
}
38+
}
39+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace WebApiContrib.Core.Yaml.Tests
2+
{
3+
public class Book
4+
{
5+
public static Book[] Data = new[]
6+
{
7+
new Book { Title = "Our Mathematical Universe: My Quest for the Ultimate Nature of Reality", Author = "Max Tegmark"},
8+
new Book { Title = "Hockey Towns", Author = "Ron MacLean"},
9+
};
10+
11+
public string Title { get; set; }
12+
13+
public string Author { get; set; }
14+
}
15+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
namespace WebApiContrib.Core.Yaml.Tests
4+
{
5+
[Route("api/books")]
6+
public class BooksController : ControllerBase
7+
{
8+
[HttpGet]
9+
public IActionResult Get()
10+
{
11+
return Ok(Book.Data);
12+
}
13+
14+
[HttpGet("{id}")]
15+
public IActionResult Get(int id)
16+
{
17+
if (Book.Data.Length >= id && id > 0)
18+
{
19+
return Ok(Book.Data[id-1]);
20+
}
21+
22+
return Ok(null);
23+
}
24+
25+
[HttpPost]
26+
public IActionResult Post([FromBody]Book book)
27+
{
28+
return Ok(book);
29+
}
30+
}
31+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.Extensions.Configuration;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WebApiContrib.Core.Formatter.Yaml;
6+
7+
namespace WebApiContrib.Core.Yaml.Tests
8+
{
9+
public class Startup
10+
{
11+
public IConfigurationRoot Configuration { get; }
12+
13+
public Startup(IHostingEnvironment env)
14+
{
15+
var builder = new ConfigurationBuilder()
16+
.SetBasePath(env.ContentRootPath)
17+
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
18+
.AddEnvironmentVariables();
19+
Configuration = builder.Build();
20+
}
21+
22+
public void ConfigureServices(IServiceCollection services)
23+
{
24+
services.AddMvcCore()
25+
.AddJsonFormatters()
26+
.AddYamlFormatters();
27+
}
28+
29+
public void Configure(IApplicationBuilder app)
30+
{
31+
app.UseMvc();
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)