From 202b2d741a5878d2b49aab8d5edcb6b280f9e658 Mon Sep 17 00:00:00 2001 From: Aaron Roney Date: Fri, 21 Dec 2018 02:20:59 -0800 Subject: [PATCH] Pass HttpContext into lambda. --- src/Core/AspNetCore.Proxy.csproj | 2 +- src/Core/ProxyRouteExtensions.cs | 105 ++++++++++++++++++++++++++++--- src/Test/UnitTests.cs | 102 +++++++++++++++++++++++++++--- 3 files changed, 194 insertions(+), 15 deletions(-) diff --git a/src/Core/AspNetCore.Proxy.csproj b/src/Core/AspNetCore.Proxy.csproj index 9f440e9..9e4c150 100644 --- a/src/Core/AspNetCore.Proxy.csproj +++ b/src/Core/AspNetCore.Proxy.csproj @@ -1,6 +1,6 @@ - 1.2.2 + 1.3.0 AspNetCore.Proxy AspNetCore.Proxy bin\AspNetCore.Proxy.xml diff --git a/src/Core/ProxyRouteExtensions.cs b/src/Core/ProxyRouteExtensions.cs index df1fb01..df9d3eb 100644 --- a/src/Core/ProxyRouteExtensions.cs +++ b/src/Core/ProxyRouteExtensions.cs @@ -28,8 +28,8 @@ public static void UseProxies(this IApplicationBuilder app) var attribute = method.GetCustomAttributes(typeof(ProxyRouteAttribute), false).First() as ProxyRouteAttribute; var parameters = method.GetParameters(); - if(!(method.ReturnType == typeof(Task))) - throw new InvalidOperationException($"Proxied generator method ({name}) must return a Task."); + if(method.ReturnType != typeof(Task) && method.ReturnType != typeof(string)) + throw new InvalidOperationException($"Proxied generator method ({name}) must return a `Task` or `string`."); if(!method.IsStatic) throw new InvalidOperationException($"Proxied generator method ({name}) must be static."); @@ -49,7 +49,12 @@ public static void UseProxies(this IApplicationBuilder app) } }); - return method.Invoke(null, castedArgs.ToArray()) as Task; + // Make sure to always return a `Task`, but allow methods that just return a `string`. + + if(method.ReturnType == typeof(Task)) + return method.Invoke(null, castedArgs.ToArray()) as Task; + + return Task.FromResult(method.Invoke(null, castedArgs.ToArray()) as string); }); } } @@ -59,16 +64,16 @@ public static void UseProxies(this IApplicationBuilder app) /// /// The ASP.NET . /// The local route endpoint. - /// A functor which returns the address to which the request is proxied. - /// A catch all for failures. - public static void UseProxy(this IApplicationBuilder app, string endpoint, Func, Task> getProxiedAddress, Func onFailure = null) + /// A lambda { (context, args) => Task[string] } which returns the address to which the request is proxied. + /// A lambda to handle proxy failures { (context, exception) => Task }. + public static void UseProxy(this IApplicationBuilder app, string endpoint, Func, Task> getProxiedAddress, Func onFailure = null) { app.UseRouter(builder => { builder.MapMiddlewareRoute(endpoint, proxyApp => { proxyApp.Run(async context => { try { - var proxiedAddress = await getProxiedAddress(context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)).ConfigureAwait(false); + var proxiedAddress = await getProxiedAddress(context, context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)).ConfigureAwait(false); var proxiedResponse = await context.SendProxyHttpRequest(proxiedAddress).ConfigureAwait(false); await context.CopyProxyHttpResponse(proxiedResponse).ConfigureAwait(false); @@ -89,5 +94,91 @@ public static void UseProxy(this IApplicationBuilder app, string endpoint, Func< }); }); } + + #region UseProxy Overloads + + /// + /// Middleware which creates an ad hoc proxy over a specified endpoint. + /// + /// The ASP.NET . + /// The local route endpoint. + /// A lambda { (args) => Task[string] } which returns the address to which the request is proxied. + /// A lambda to handle proxy failures { (context, exception) => Task }. + public static void UseProxy(this IApplicationBuilder app, string endpoint, Func, Task> getProxiedAddress, Func onFailure = null) + { + Func, Task> gpa = (context, args) => getProxiedAddress(args); + + UseProxy(app, endpoint, gpa, onFailure); + } + + /// + /// Middleware which creates an ad hoc proxy over a specified endpoint. + /// + /// The ASP.NET . + /// The local route endpoint. + /// A lambda { () => Task[string] } which returns the address to which the request is proxied. + /// A lambda to handle proxy failures { (context, exception) => Task }. + public static void UseProxy(this IApplicationBuilder app, string endpoint, Func> getProxiedAddress, Func onFailure = null) + { + Func, Task> gpa = (context, args) => getProxiedAddress(); + + UseProxy(app, endpoint, gpa, onFailure); + } + + /// + /// Middleware which creates an ad hoc proxy over a specified endpoint. + /// + /// The ASP.NET . + /// The local route endpoint. + /// A lambda { (context, args) => string } which returns the address to which the request is proxied. + /// A lambda to handle proxy failures { (context, exception) => void }. + public static void UseProxy(this IApplicationBuilder app, string endpoint, Func, string> getProxiedAddress, Action onFailure = null) + { + Func, Task> gpa = (context, args) => Task.FromResult(getProxiedAddress(context, args)); + + Func of = null; + if(onFailure != null) + of = (context, e) => { onFailure(context, e); return Task.FromResult(0); }; + + UseProxy(app, endpoint, gpa, of); + } + + /// + /// Middleware which creates an ad hoc proxy over a specified endpoint. + /// + /// The ASP.NET . + /// The local route endpoint. + /// A lambda { (args) => string } which returns the address to which the request is proxied. + /// A lambda to handle proxy failures { (context, exception) => void }. + public static void UseProxy(this IApplicationBuilder app, string endpoint, Func, string> getProxiedAddress, Action onFailure = null) + { + Func, Task> gpa = (context, args) => Task.FromResult(getProxiedAddress(args)); + + Func of = null; + if(onFailure != null) + of = (context, e) => { onFailure(context, e); return Task.FromResult(0); }; + + UseProxy(app, endpoint, gpa, of); + } + + /// + /// Middleware which creates an ad hoc proxy over a specified endpoint. + /// + /// The ASP.NET . + /// The local route endpoint. + /// A lambda { () => string } which returns the address to which the request is proxied. + /// A lambda to handle proxy failures { (context, exception) => void }. + public static void UseProxy(this IApplicationBuilder app, string endpoint, Func getProxiedAddress, Action onFailure = null) + { + Func, Task> gpa = (context, args) => Task.FromResult(getProxiedAddress()); + + Func of = null; + if(onFailure != null) + of = (context, e) => { onFailure(context, e); return Task.FromResult(0); }; + + UseProxy(app, endpoint, gpa, of); + } + + #endregion } } \ No newline at end of file diff --git a/src/Test/UnitTests.cs b/src/Test/UnitTests.cs index 1b9bc74..4f763fc 100644 --- a/src/Test/UnitTests.cs +++ b/src/Test/UnitTests.cs @@ -23,9 +23,9 @@ public UnitTests() } [Fact] - public async Task ProxyAttribute() + public async Task ProxyAttributeToTask() { - var response = await _client.GetAsync("api/posts/1"); + var response = await _client.GetAsync("api/posts/totask/1"); response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); @@ -33,9 +33,69 @@ public async Task ProxyAttribute() } [Fact] - public async Task ProxyMiddleware() + public async Task ProxyAttributeToString() { - var response = await _client.GetAsync("api/comments/1"); + var response = await _client.GetAsync("api/posts/tostring/1"); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + Assert.Contains("sunt aut facere repellat provident occaecati excepturi optio reprehenderit", JObject.Parse(responseString).Value("title")); + } + + [Fact] + public async Task ProxyMiddlewareWithContextAndArgsToTask() + { + var response = await _client.GetAsync("api/comments/contextandargstotask/1"); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value("name")); + } + + [Fact] + public async Task ProxyMiddlewareWithArgsToTask() + { + var response = await _client.GetAsync("api/comments/argstotask/1"); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value("name")); + } + + [Fact] + public async Task ProxyMiddlewareWithEmptyToTask() + { + var response = await _client.GetAsync("api/comments/emptytotask"); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value("name")); + } + + [Fact] + public async Task ProxyMiddlewareWithContextAndArgsToString() + { + var response = await _client.GetAsync("api/comments/contextandargstostring/1"); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value("name")); + } + + [Fact] + public async Task ProxyMiddlewareWithArgsToString() + { + var response = await _client.GetAsync("api/comments/argstostring/1"); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value("name")); + } + + [Fact] + public async Task ProxyMiddlewareWithEmptyToString() + { + var response = await _client.GetAsync("api/comments/emptytostring"); response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); @@ -54,18 +114,46 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseProxies(); - app.UseProxy("api/comments/{postId}", (args) => { + app.UseProxy("api/comments/contextandargstotask/{postId}", (context, args) => { + context.GetHashCode(); + return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/{args["postId"]}"); + }); + + app.UseProxy("api/comments/argstotask/{postId}", (args) => { return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/{args["postId"]}"); }); + + app.UseProxy("api/comments/emptytotask", () => { + return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/1"); + }); + + app.UseProxy("api/comments/contextandargstostring/{postId}", (context, args) => { + context.GetHashCode(); + return $"https://jsonplaceholder.typicode.com/comments/{args["postId"]}"; + }); + + app.UseProxy("api/comments/argstostring/{postId}", (args) => { + return $"https://jsonplaceholder.typicode.com/comments/{args["postId"]}"; + }); + + app.UseProxy("api/comments/emptytostring", () => { + return $"https://jsonplaceholder.typicode.com/comments/1"; + }); } } public static class UseProxies { - [ProxyRoute("api/posts/{postId}")] - public static Task ProxyGoogle(int postId) + [ProxyRoute("api/posts/totask/{postId}")] + public static Task ProxyGoogleToTask(int postId) { return Task.FromResult($"https://jsonplaceholder.typicode.com/posts/{postId}"); } + + [ProxyRoute("api/posts/tostring/{postId}")] + public static string ProxyGoogleToString(int postId) + { + return $"https://jsonplaceholder.typicode.com/posts/{postId}"; + } } }