Skip to content

Commit 54933e4

Browse files
authored
Merge pull request #3 from twitchax/context_fix
Pass HttpContext into lambda.
2 parents 974f771 + 202b2d7 commit 54933e4

File tree

3 files changed

+194
-15
lines changed

3 files changed

+194
-15
lines changed

src/Core/AspNetCore.Proxy.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<Version>1.2.2</Version>
3+
<Version>1.3.0</Version>
44
<AssemblyName>AspNetCore.Proxy</AssemblyName>
55
<PackageId>AspNetCore.Proxy</PackageId>
66
<DocumentationFile>bin\AspNetCore.Proxy.xml</DocumentationFile>

src/Core/ProxyRouteExtensions.cs

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public static void UseProxies(this IApplicationBuilder app)
2828
var attribute = method.GetCustomAttributes(typeof(ProxyRouteAttribute), false).First() as ProxyRouteAttribute;
2929
var parameters = method.GetParameters();
3030

31-
if(!(method.ReturnType == typeof(Task<string>)))
32-
throw new InvalidOperationException($"Proxied generator method ({name}) must return a Task<string>.");
31+
if(method.ReturnType != typeof(Task<string>) && method.ReturnType != typeof(string))
32+
throw new InvalidOperationException($"Proxied generator method ({name}) must return a `Task<string>` or `string`.");
3333

3434
if(!method.IsStatic)
3535
throw new InvalidOperationException($"Proxied generator method ({name}) must be static.");
@@ -49,7 +49,12 @@ public static void UseProxies(this IApplicationBuilder app)
4949
}
5050
});
5151

52-
return method.Invoke(null, castedArgs.ToArray()) as Task<string>;
52+
// Make sure to always return a `Task<string>`, but allow methods that just return a `string`.
53+
54+
if(method.ReturnType == typeof(Task<string>))
55+
return method.Invoke(null, castedArgs.ToArray()) as Task<string>;
56+
57+
return Task.FromResult(method.Invoke(null, castedArgs.ToArray()) as string);
5358
});
5459
}
5560
}
@@ -59,16 +64,16 @@ public static void UseProxies(this IApplicationBuilder app)
5964
/// </summary>
6065
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
6166
/// <param name="endpoint">The local route endpoint.</param>
62-
/// <param name="getProxiedAddress">A functor which returns the address to which the request is proxied.</param>
63-
/// <param name="onFailure">A catch all for failures.</param>
64-
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<IDictionary<string, object>, Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
67+
/// <param name="getProxiedAddress">A lambda { (context, args) => Task[string] } which returns the address to which the request is proxied.</param>
68+
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => Task }.</param>
69+
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<HttpContext, IDictionary<string, object>, Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
6570
{
6671
app.UseRouter(builder => {
6772
builder.MapMiddlewareRoute(endpoint, proxyApp => {
6873
proxyApp.Run(async context => {
6974
try
7075
{
71-
var proxiedAddress = await getProxiedAddress(context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)).ConfigureAwait(false);
76+
var proxiedAddress = await getProxiedAddress(context, context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)).ConfigureAwait(false);
7277
var proxiedResponse = await context.SendProxyHttpRequest(proxiedAddress).ConfigureAwait(false);
7378

7479
await context.CopyProxyHttpResponse(proxiedResponse).ConfigureAwait(false);
@@ -89,5 +94,91 @@ public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<
8994
});
9095
});
9196
}
97+
98+
#region UseProxy Overloads
99+
100+
/// <summary>
101+
/// Middleware which creates an ad hoc proxy over a specified endpoint.
102+
/// </summary>
103+
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
104+
/// <param name="endpoint">The local route endpoint.</param>
105+
/// <param name="getProxiedAddress">A lambda { (args) => Task[string] } which returns the address to which the request is proxied.</param>
106+
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => Task }.</param>
107+
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<IDictionary<string, object>, Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
108+
{
109+
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => getProxiedAddress(args);
110+
111+
UseProxy(app, endpoint, gpa, onFailure);
112+
}
113+
114+
/// <summary>
115+
/// Middleware which creates an ad hoc proxy over a specified endpoint.
116+
/// </summary>
117+
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
118+
/// <param name="endpoint">The local route endpoint.</param>
119+
/// <param name="getProxiedAddress">A lambda { () => Task[string] } which returns the address to which the request is proxied.</param>
120+
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => Task }.</param>
121+
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
122+
{
123+
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => getProxiedAddress();
124+
125+
UseProxy(app, endpoint, gpa, onFailure);
126+
}
127+
128+
/// <summary>
129+
/// Middleware which creates an ad hoc proxy over a specified endpoint.
130+
/// </summary>
131+
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
132+
/// <param name="endpoint">The local route endpoint.</param>
133+
/// <param name="getProxiedAddress">A lambda { (context, args) => string } which returns the address to which the request is proxied.</param>
134+
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => void }.</param>
135+
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<HttpContext, IDictionary<string, object>, string> getProxiedAddress, Action<HttpContext, Exception> onFailure = null)
136+
{
137+
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => Task.FromResult(getProxiedAddress(context, args));
138+
139+
Func<HttpContext, Exception, Task> of = null;
140+
if(onFailure != null)
141+
of = (context, e) => { onFailure(context, e); return Task.FromResult(0); };
142+
143+
UseProxy(app, endpoint, gpa, of);
144+
}
145+
146+
/// <summary>
147+
/// Middleware which creates an ad hoc proxy over a specified endpoint.
148+
/// </summary>
149+
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
150+
/// <param name="endpoint">The local route endpoint.</param>
151+
/// <param name="getProxiedAddress">A lambda { (args) => string } which returns the address to which the request is proxied.</param>
152+
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => void }.</param>
153+
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<IDictionary<string, object>, string> getProxiedAddress, Action<HttpContext, Exception> onFailure = null)
154+
{
155+
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => Task.FromResult(getProxiedAddress(args));
156+
157+
Func<HttpContext, Exception, Task> of = null;
158+
if(onFailure != null)
159+
of = (context, e) => { onFailure(context, e); return Task.FromResult(0); };
160+
161+
UseProxy(app, endpoint, gpa, of);
162+
}
163+
164+
/// <summary>
165+
/// Middleware which creates an ad hoc proxy over a specified endpoint.
166+
/// </summary>
167+
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
168+
/// <param name="endpoint">The local route endpoint.</param>
169+
/// <param name="getProxiedAddress">A lambda { () => string } which returns the address to which the request is proxied.</param>
170+
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => void }.</param>
171+
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<string> getProxiedAddress, Action<HttpContext, Exception> onFailure = null)
172+
{
173+
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => Task.FromResult(getProxiedAddress());
174+
175+
Func<HttpContext, Exception, Task> of = null;
176+
if(onFailure != null)
177+
of = (context, e) => { onFailure(context, e); return Task.FromResult(0); };
178+
179+
UseProxy(app, endpoint, gpa, of);
180+
}
181+
182+
#endregion
92183
}
93184
}

src/Test/UnitTests.cs

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,79 @@ public UnitTests()
2323
}
2424

2525
[Fact]
26-
public async Task ProxyAttribute()
26+
public async Task ProxyAttributeToTask()
2727
{
28-
var response = await _client.GetAsync("api/posts/1");
28+
var response = await _client.GetAsync("api/posts/totask/1");
2929
response.EnsureSuccessStatusCode();
3030

3131
var responseString = await response.Content.ReadAsStringAsync();
3232
Assert.Contains("sunt aut facere repellat provident occaecati excepturi optio reprehenderit", JObject.Parse(responseString).Value<string>("title"));
3333
}
3434

3535
[Fact]
36-
public async Task ProxyMiddleware()
36+
public async Task ProxyAttributeToString()
3737
{
38-
var response = await _client.GetAsync("api/comments/1");
38+
var response = await _client.GetAsync("api/posts/tostring/1");
39+
response.EnsureSuccessStatusCode();
40+
41+
var responseString = await response.Content.ReadAsStringAsync();
42+
Assert.Contains("sunt aut facere repellat provident occaecati excepturi optio reprehenderit", JObject.Parse(responseString).Value<string>("title"));
43+
}
44+
45+
[Fact]
46+
public async Task ProxyMiddlewareWithContextAndArgsToTask()
47+
{
48+
var response = await _client.GetAsync("api/comments/contextandargstotask/1");
49+
response.EnsureSuccessStatusCode();
50+
51+
var responseString = await response.Content.ReadAsStringAsync();
52+
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
53+
}
54+
55+
[Fact]
56+
public async Task ProxyMiddlewareWithArgsToTask()
57+
{
58+
var response = await _client.GetAsync("api/comments/argstotask/1");
59+
response.EnsureSuccessStatusCode();
60+
61+
var responseString = await response.Content.ReadAsStringAsync();
62+
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
63+
}
64+
65+
[Fact]
66+
public async Task ProxyMiddlewareWithEmptyToTask()
67+
{
68+
var response = await _client.GetAsync("api/comments/emptytotask");
69+
response.EnsureSuccessStatusCode();
70+
71+
var responseString = await response.Content.ReadAsStringAsync();
72+
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
73+
}
74+
75+
[Fact]
76+
public async Task ProxyMiddlewareWithContextAndArgsToString()
77+
{
78+
var response = await _client.GetAsync("api/comments/contextandargstostring/1");
79+
response.EnsureSuccessStatusCode();
80+
81+
var responseString = await response.Content.ReadAsStringAsync();
82+
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
83+
}
84+
85+
[Fact]
86+
public async Task ProxyMiddlewareWithArgsToString()
87+
{
88+
var response = await _client.GetAsync("api/comments/argstostring/1");
89+
response.EnsureSuccessStatusCode();
90+
91+
var responseString = await response.Content.ReadAsStringAsync();
92+
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
93+
}
94+
95+
[Fact]
96+
public async Task ProxyMiddlewareWithEmptyToString()
97+
{
98+
var response = await _client.GetAsync("api/comments/emptytostring");
3999
response.EnsureSuccessStatusCode();
40100

41101
var responseString = await response.Content.ReadAsStringAsync();
@@ -54,18 +114,46 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
54114
{
55115
app.UseProxies();
56116

57-
app.UseProxy("api/comments/{postId}", (args) => {
117+
app.UseProxy("api/comments/contextandargstotask/{postId}", (context, args) => {
118+
context.GetHashCode();
119+
return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/{args["postId"]}");
120+
});
121+
122+
app.UseProxy("api/comments/argstotask/{postId}", (args) => {
58123
return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/{args["postId"]}");
59124
});
125+
126+
app.UseProxy("api/comments/emptytotask", () => {
127+
return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/1");
128+
});
129+
130+
app.UseProxy("api/comments/contextandargstostring/{postId}", (context, args) => {
131+
context.GetHashCode();
132+
return $"https://jsonplaceholder.typicode.com/comments/{args["postId"]}";
133+
});
134+
135+
app.UseProxy("api/comments/argstostring/{postId}", (args) => {
136+
return $"https://jsonplaceholder.typicode.com/comments/{args["postId"]}";
137+
});
138+
139+
app.UseProxy("api/comments/emptytostring", () => {
140+
return $"https://jsonplaceholder.typicode.com/comments/1";
141+
});
60142
}
61143
}
62144

63145
public static class UseProxies
64146
{
65-
[ProxyRoute("api/posts/{postId}")]
66-
public static Task<string> ProxyGoogle(int postId)
147+
[ProxyRoute("api/posts/totask/{postId}")]
148+
public static Task<string> ProxyGoogleToTask(int postId)
67149
{
68150
return Task.FromResult($"https://jsonplaceholder.typicode.com/posts/{postId}");
69151
}
152+
153+
[ProxyRoute("api/posts/tostring/{postId}")]
154+
public static string ProxyGoogleToString(int postId)
155+
{
156+
return $"https://jsonplaceholder.typicode.com/posts/{postId}";
157+
}
70158
}
71159
}

0 commit comments

Comments
 (0)