Skip to content

Commit 5d3dc2a

Browse files
committed
Add SwaggerMiddleware(WIP)
1 parent 8e87540 commit 5d3dc2a

21 files changed

+1318
-7
lines changed

sandbox/WebHostingApp/Program.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.AspNetCore.Hosting;
2+
using System.Linq;
23
using Microsoft.Extensions.Logging;
34
using MicroBatchFramework;
45
using System;
@@ -25,19 +26,46 @@ public void Sum(int x, int y)
2526
{
2627
Context.Logger.LogInformation((x + y).ToString());
2728
}
29+
public void SimpleString(string input)
30+
{
31+
Context.Logger.LogInformation("In: " + input);
32+
}
33+
public void SimpleObject(Person person)
34+
{
35+
Context.Logger.LogInformation(person.Name + ":" + person.Age);
36+
}
2837

2938
public void InOut(string input, Person person)
3039
{
3140
Context.Logger.LogInformation(person.Name + ":" + person.Age);
3241
Context.Logger.LogInformation("In: " + input);
3342
}
3443

44+
public void SimpleArray(int[] simpleArray)
45+
{
46+
Context.Logger.LogInformation(string.Join(", ", simpleArray));
47+
}
48+
49+
50+
public void Array(int[] intArray, string[] stringArray, Person[] objectArray, MyFruit[] fruits)
51+
{
52+
Context.Logger.LogInformation(string.Join(", ", intArray));
53+
Context.Logger.LogInformation(string.Join(", ", stringArray));
54+
Context.Logger.LogInformation(string.Join(", ", objectArray.Select(x => x.Age + ":" + x.Name)));
55+
Context.Logger.LogInformation(string.Join(", ", fruits.Select(x => x.ToString())));
56+
}
57+
3558
public void ErrorMan()
3659
{
3760
throw new Exception("foo bar baz");
3861
}
3962
}
4063

64+
public enum MyFruit
65+
{
66+
Apple, Orange, Grape
67+
}
68+
4169
public class Person
4270
{
4371
public int Age { get; set; }

src/MicroBatchFramework.WebHosting/BatchEngineHostingExtensions.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using Microsoft.Extensions.Logging;
88
using System.Reflection;
99
using System.Collections.Generic;
10+
using MicroBatchFramework.WebHosting.Swagger;
11+
using System.IO;
1012

1113
namespace MicroBatchFramework // .WebHosting
1214
{
@@ -29,10 +31,21 @@ public static IWebHostBuilder PrepareBatchEngineMiddleware(this IWebHostBuilder
2931
});
3032
}
3133

32-
public static Task RunBatchEngineWebHosting(this IWebHostBuilder builder, string urls, IBatchInterceptor interceptor = null)
34+
public static Task RunBatchEngineWebHosting(this IWebHostBuilder builder, string urls, SwaggerOptions swaggerOptions = null, IBatchInterceptor interceptor = null)
3335
{
3436
return builder
3537
.PrepareBatchEngineMiddleware(interceptor)
38+
.ConfigureServices(services =>
39+
{
40+
if (swaggerOptions == null)
41+
{
42+
var entryAsm = Assembly.GetEntryAssembly();
43+
var xmlName = entryAsm.GetName().Name + ".xml";
44+
var xmlPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), xmlName);
45+
swaggerOptions = new SwaggerOptions(entryAsm.GetName().Name, "", "/") { XmlDocumentPath = xmlPath };
46+
}
47+
services.AddSingleton<SwaggerOptions>(swaggerOptions);
48+
})
3649
.UseKestrel()
3750
.UseUrls(urls)
3851
.UseStartup<DefaultStartup>()
@@ -45,6 +58,11 @@ public static IApplicationBuilder UseBatchEngineMiddleware(this IApplicationBuil
4558
return builder.UseMiddleware<BatchEngineMiddleware>();
4659
}
4760

61+
public static IApplicationBuilder UseBatchEngineSwaggerMiddleware(this IApplicationBuilder builder, SwaggerOptions options)
62+
{
63+
return builder.UseMiddleware<BatchEngineSwaggerMiddleware>(options);
64+
}
65+
4866
public class DefaultStartup
4967
{
5068
public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime)
@@ -71,6 +89,8 @@ public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime)
7189
catch { }
7290
});
7391

92+
var swaggerOption = app.ApplicationServices.GetService<SwaggerOptions>() ?? new SwaggerOptions("MicroBatchFramework", "", "/");
93+
app.UseBatchEngineSwaggerMiddleware(swaggerOption);
7494
app.UseBatchEngineMiddleware();
7595
}
7696
}

src/MicroBatchFramework.WebHosting/BatchEngineMiddleware.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,27 @@ public async Task Invoke(HttpContext httpContext)
118118
string[] args = null;
119119
try
120120
{
121-
args = new string[(httpContext.Request.Form.Count * 2) + 1];
121+
if (httpContext.Request.HasFormContentType)
122122
{
123-
var i = 0;
124-
args[i++] = methodInfo.DeclaringType.Name + "." + methodInfo.Name;
125-
foreach (var item in httpContext.Request.Form)
123+
args = new string[(httpContext.Request.Form.Count * 2) + 1];
126124
{
127-
args[i++] = "-" + item.Key;
128-
args[i++] = (item.Value.Count == 0) ? null : item.Value[0];
125+
var i = 0;
126+
args[i++] = methodInfo.DeclaringType.Name + "." + methodInfo.Name;
127+
foreach (var item in httpContext.Request.Form)
128+
{
129+
args[i++] = "-" + item.Key;
130+
args[i++] = (item.Value.Count == 0) ? null : item.Value[0];
131+
}
129132
}
130133
}
134+
else
135+
{
136+
args = new[] { methodInfo.DeclaringType.Name + "." + methodInfo.Name };
137+
}
131138
}
132139
catch (Exception ex)
133140
{
141+
httpContext.Response.ContentType = "text/plain";
134142
httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
135143
await httpContext.Response.WriteAsync(ex.ToString());
136144
return;
@@ -146,13 +154,15 @@ public async Task Invoke(HttpContext httpContext)
146154
// out result
147155
if (hostingInterceptor.CompleteSuccessfully)
148156
{
157+
httpContext.Response.ContentType = "text/plain";
149158
httpContext.Response.StatusCode = (int)HttpStatusCode.OK;
150159
await httpContext.Response.WriteAsync(collectLogger.ToString());
151160
}
152161
else
153162
{
154163
var errorMsg = ((hostingInterceptor.ErrorMessage != null) ? hostingInterceptor.ErrorMessage + Environment.NewLine : "")
155164
+ ((hostingInterceptor.Exception != null) ? hostingInterceptor.Exception.ToString() : "");
165+
httpContext.Response.ContentType = "text/plain";
156166
httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
157167
await httpContext.Response.WriteAsync(errorMsg);
158168
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using MicroBatchFramework.WebHosting.Swagger;
2+
using System.Linq;
3+
using Microsoft.AspNetCore.Http;
4+
using Microsoft.Extensions.Options;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Reflection;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using System.IO;
11+
12+
namespace MicroBatchFramework.WebHosting
13+
{
14+
public class BatchEngineSwaggerMiddleware
15+
{
16+
static readonly Task EmptyTask = Task.FromResult(0);
17+
18+
readonly RequestDelegate next;
19+
readonly MethodInfo[] handlers;
20+
readonly SwaggerOptions options;
21+
22+
public BatchEngineSwaggerMiddleware(RequestDelegate next, TargetBatchTypeCollection targetTypes, SwaggerOptions options)
23+
{
24+
this.next = next;
25+
this.handlers = targetTypes.SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)).ToArray();
26+
this.options = options;
27+
}
28+
29+
public Task Invoke(HttpContext httpContext)
30+
{
31+
// reference embedded resouces
32+
const string prefix = "MicroBatchFramework.WebHosting.Swagger.SwaggerUI.";
33+
34+
var path = httpContext.Request.Path.Value.Trim('/');
35+
if (path == "") path = "index.html";
36+
var filePath = prefix + path.Replace("/", ".");
37+
var mediaType = GetMediaType(filePath);
38+
39+
if (path.EndsWith(options.JsonName))
40+
{
41+
var builder = new SwaggerDefinitionBuilder(options, httpContext, handlers);
42+
var bytes = builder.BuildSwaggerJson();
43+
httpContext.Response.Headers["Content-Type"] = new[] { "application/json" };
44+
httpContext.Response.StatusCode = 200;
45+
httpContext.Response.Body.Write(bytes, 0, bytes.Length);
46+
return EmptyTask;
47+
}
48+
49+
var myAssembly = typeof(BatchEngineSwaggerMiddleware).GetTypeInfo().Assembly;
50+
51+
using (var stream = myAssembly.GetManifestResourceStream(filePath))
52+
{
53+
if (options.ResolveCustomResource == null)
54+
{
55+
if (stream == null)
56+
{
57+
// not found, standard request.
58+
return next(httpContext);
59+
}
60+
61+
httpContext.Response.Headers["Content-Type"] = new[] { mediaType };
62+
httpContext.Response.StatusCode = 200;
63+
var response = httpContext.Response.Body;
64+
stream.CopyTo(response);
65+
}
66+
else
67+
{
68+
byte[] bytes;
69+
if (stream == null)
70+
{
71+
bytes = options.ResolveCustomResource(path, null);
72+
}
73+
else
74+
{
75+
using (var ms = new MemoryStream())
76+
{
77+
stream.CopyTo(ms);
78+
bytes = options.ResolveCustomResource(path, ms.ToArray());
79+
}
80+
}
81+
82+
if (bytes == null)
83+
{
84+
// not found, standard request.
85+
return next(httpContext);
86+
}
87+
88+
httpContext.Response.Headers["Content-Type"] = new[] { mediaType };
89+
httpContext.Response.StatusCode = 200;
90+
var response = httpContext.Response.Body;
91+
response.Write(bytes, 0, bytes.Length);
92+
}
93+
}
94+
95+
96+
return EmptyTask;
97+
}
98+
99+
static string GetMediaType(string path)
100+
{
101+
var extension = path.Split('.').Last();
102+
103+
switch (extension)
104+
{
105+
case "css":
106+
return "text/css";
107+
case "js":
108+
return "text/javascript";
109+
case "json":
110+
return "application/json";
111+
case "gif":
112+
return "image/gif";
113+
case "png":
114+
return "image/png";
115+
case "eot":
116+
return "application/vnd.ms-fontobject";
117+
case "woff":
118+
return "application/font-woff";
119+
case "woff2":
120+
return "application/font-woff2";
121+
case "otf":
122+
return "application/font-sfnt";
123+
case "ttf":
124+
return "application/font-sfnt";
125+
case "svg":
126+
return "image/svg+xml";
127+
case "ico":
128+
return "image/x-icon";
129+
default:
130+
return "text/html";
131+
}
132+
}
133+
}
134+
}

src/MicroBatchFramework.WebHosting/MicroBatchFramework.WebHosting.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
<TargetFramework>netstandard2.0</TargetFramework>
55
</PropertyGroup>
66

7+
<ItemGroup>
8+
<EmbeddedResource Include="Swagger\SwaggerUI\*" />
9+
</ItemGroup>
10+
711
<ItemGroup>
812
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
913
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />

0 commit comments

Comments
 (0)