Skip to content

Commit 784d86c

Browse files
Merge pull request #303 from max-ieremenko/release/1.11.1
release 1.11.1
2 parents 96fcb24 + a90b531 commit 784d86c

File tree

6 files changed

+190
-56
lines changed

6 files changed

+190
-56
lines changed

Sources/ServiceModel.Grpc.AspNetCore.NSwag.Test/MultipurposeServiceTest.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
// </copyright>
1616

1717
using System.IO.Compression;
18+
using System.Net;
1819
using Microsoft.AspNetCore.Builder;
1920
using Microsoft.AspNetCore.Server.Kestrel.Core;
2021
using Microsoft.Extensions.DependencyInjection;
2122
using NUnit.Framework;
2223
using ServiceModel.Grpc.AspNetCore.TestApi;
24+
using ServiceModel.Grpc.AspNetCore.TestApi.Domain;
2325
using ServiceModel.Grpc.TestApi.Domain;
2426

2527
namespace ServiceModel.Grpc.AspNetCore.NSwag;
@@ -44,6 +46,7 @@ public async Task BeforeAll()
4446
services.AddServiceModelGrpcSwagger();
4547
services.AddOpenApiDocument();
4648
services.AddMvc();
49+
services.AddTransient<CustomResponseMiddleware>();
4750
})
4851
.ConfigureApp(app =>
4952
{
@@ -52,6 +55,8 @@ public async Task BeforeAll()
5255
app.UseReDoc(); // serve ReDoc UI
5356

5457
app.UseServiceModelGrpcSwaggerGateway();
58+
59+
app.UseMiddleware<CustomResponseMiddleware>();
5560
})
5661
.ConfigureEndpoints(endpoints =>
5762
{
@@ -123,4 +128,43 @@ public async Task Sum5ValuesAsync()
123128

124129
actual.ShouldBe(15);
125130
}
131+
132+
[Test]
133+
public async Task BlockingCallAsync()
134+
{
135+
var parameters = new Dictionary<string, object>
136+
{
137+
{ "x", 1 },
138+
{ "y", "a" }
139+
};
140+
141+
var actual = await _client.InvokeAsync<string>(nameof(IMultipurposeService.BlockingCallAsync), parameters);
142+
143+
actual.ShouldBe("a1");
144+
}
145+
146+
[Test]
147+
[TestCase(HttpStatusCode.Unauthorized, "application/grpc")]
148+
[TestCase(HttpStatusCode.OK, "text/plain")]
149+
public async Task NonGrpcResponseAsync(HttpStatusCode statusCode, string contentType)
150+
{
151+
var headers = new Dictionary<string, string>
152+
{
153+
{ CustomResponseMiddleware.HeaderResponseStatusCode, statusCode.ToString() },
154+
{ CustomResponseMiddleware.HeaderContentType, contentType },
155+
{ CustomResponseMiddleware.HeaderResponseBody, "some message" }
156+
};
157+
158+
var parameters = new Dictionary<string, object>
159+
{
160+
{ "x", 1 },
161+
{ "y", "a" }
162+
};
163+
164+
var response = await _client.PostAsync(nameof(IMultipurposeService.BlockingCallAsync), parameters, headers);
165+
166+
response.StatusCode.ShouldBe(statusCode);
167+
response.Content.Headers.ContentType!.MediaType.ShouldBe(contentType);
168+
(await response.Content.ReadAsStringAsync()).ShouldBe("some message");
169+
}
126170
}

Sources/ServiceModel.Grpc.AspNetCore.Swashbuckle.Test/MultipurposeServiceTest.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
// </copyright>
1616

1717
using System.IO.Compression;
18+
using System.Net;
1819
using Microsoft.AspNetCore.Builder;
1920
using Microsoft.AspNetCore.Server.Kestrel.Core;
2021
using Microsoft.Extensions.DependencyInjection;
2122
using Microsoft.OpenApi.Models;
2223
using NUnit.Framework;
2324
using ServiceModel.Grpc.AspNetCore.TestApi;
25+
using ServiceModel.Grpc.AspNetCore.TestApi.Domain;
2426
using ServiceModel.Grpc.TestApi.Domain;
2527
using OpenApiDocument = ServiceModel.Grpc.AspNetCore.TestApi.OpenApiDocument;
2628

@@ -50,6 +52,7 @@ public async Task BeforeAll()
5052
c.EnableAnnotations(true, true);
5153
});
5254
services.AddMvc();
55+
services.AddTransient<CustomResponseMiddleware>();
5356
})
5457
.ConfigureApp(app =>
5558
{
@@ -60,6 +63,8 @@ public async Task BeforeAll()
6063
});
6164

6265
app.UseServiceModelGrpcSwaggerGateway();
66+
67+
app.UseMiddleware<CustomResponseMiddleware>();
6368
})
6469
.ConfigureEndpoints(endpoints =>
6570
{
@@ -131,4 +136,43 @@ public async Task Sum5ValuesAsync()
131136

132137
actual.ShouldBe(15);
133138
}
139+
140+
[Test]
141+
public async Task BlockingCallAsync()
142+
{
143+
var parameters = new Dictionary<string, object>
144+
{
145+
{ "x", 1 },
146+
{ "y", "a" }
147+
};
148+
149+
var actual = await _client.InvokeAsync<string>(nameof(IMultipurposeService.BlockingCallAsync), parameters);
150+
151+
actual.ShouldBe("a1");
152+
}
153+
154+
[Test]
155+
[TestCase(HttpStatusCode.Unauthorized, "application/grpc")]
156+
[TestCase(HttpStatusCode.OK, "text/plain")]
157+
public async Task NonGrpcResponseAsync(HttpStatusCode statusCode, string contentType)
158+
{
159+
var headers = new Dictionary<string, string>
160+
{
161+
{ CustomResponseMiddleware.HeaderResponseStatusCode, statusCode.ToString() },
162+
{ CustomResponseMiddleware.HeaderContentType, contentType },
163+
{ CustomResponseMiddleware.HeaderResponseBody, "some message" }
164+
};
165+
166+
var parameters = new Dictionary<string, object>
167+
{
168+
{ "x", 1 },
169+
{ "y", "a" }
170+
};
171+
172+
var response = await _client.PostAsync(nameof(IMultipurposeService.BlockingCallAsync), parameters, headers);
173+
174+
response.StatusCode.ShouldBe(statusCode);
175+
response.Content.Headers.ContentType!.MediaType.ShouldBe(contentType);
176+
(await response.Content.ReadAsStringAsync()).ShouldBe("some message");
177+
}
134178
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// <copyright>
2+
// Copyright Max Ieremenko
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
using System.Net;
18+
19+
namespace ServiceModel.Grpc.AspNetCore.TestApi.Domain;
20+
21+
public sealed class CustomResponseMiddleware : IMiddleware
22+
{
23+
public const string HeaderResponseStatusCode = "CustomStatusCode";
24+
public const string HeaderContentType = "CustomContentType";
25+
public const string HeaderResponseBody = "CustomBody";
26+
27+
public Task InvokeAsync(HttpContext context, RequestDelegate next)
28+
{
29+
var statusCode = context.Request.Headers[HeaderResponseStatusCode];
30+
if (statusCode.Count == 0)
31+
{
32+
return next(context);
33+
}
34+
35+
context.Response.StatusCode = (int)Enum.Parse<HttpStatusCode>(statusCode.ToString());
36+
context.Response.ContentType = context.Request.Headers[HeaderContentType].ToString();
37+
38+
var body = context.Request.Headers[HeaderResponseBody].ToString();
39+
return context.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(body)).AsTask();
40+
}
41+
}

Sources/ServiceModel.Grpc.AspNetCore.TestApi/SwaggerUiClient.cs

Lines changed: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -37,81 +37,74 @@ public SwaggerUiClient(
3737

3838
public async Task<HttpResponseHeaders> InvokeAsync(string methodName, IDictionary<string, object> parameters, IDictionary<string, string>? headers = null)
3939
{
40-
var endpoint = _document.GetEndpoint(_serviceName, methodName);
41-
var contentType = _document.GetRequestContentType(endpoint);
42-
var url = new Uri(new Uri(_hostLocation), endpoint);
40+
var response = await PostAsync(methodName, parameters, headers).ConfigureAwait(false);
4341

44-
using (var client = new HttpClient())
45-
{
46-
HttpResponseMessage response;
42+
response.EnsureSuccessStatusCode();
43+
return response.Headers;
44+
}
4745

48-
using (var request = new MemoryStream())
49-
{
50-
using (var writer = new StreamWriter(request, leaveOpen: true))
51-
{
52-
JsonSerializer.CreateDefault().Serialize(writer, parameters);
53-
}
46+
public async Task<T> InvokeAsync<T>(string methodName, IDictionary<string, object> parameters, IDictionary<string, string>? headers = null)
47+
{
48+
var response = await PostAsync(methodName, parameters, headers).ConfigureAwait(false);
5449

55-
request.Position = 0;
56-
using (var content = new StreamContent(request))
57-
{
58-
content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
59-
if (headers != null)
60-
{
61-
foreach (var entry in headers)
62-
{
63-
content.Headers.Add(entry.Key, entry.Value);
64-
}
65-
}
66-
67-
response = await client.PostAsync(url, content).ConfigureAwait(false);
68-
}
69-
}
50+
response.EnsureSuccessStatusCode();
7051

71-
response.EnsureSuccessStatusCode();
72-
return response.Headers;
73-
}
52+
var content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
53+
return JsonSerializer.CreateDefault().Deserialize<T>(new JsonTextReader(new StreamReader(content)))!;
7454
}
7555

76-
public async Task<T> InvokeAsync<T>(string methodName, IDictionary<string, object> parameters, IDictionary<string, string>? headers = null)
56+
public async Task<HttpResponseMessage> PostAsync(string methodName, IDictionary<string, object>? parameters = null, IDictionary<string, string>? headers = null)
7757
{
7858
var endpoint = _document.GetEndpoint(_serviceName, methodName);
7959
var contentType = _document.GetRequestContentType(endpoint);
8060
var url = new Uri(new Uri(_hostLocation), endpoint);
8161

82-
using (var client = new HttpClient())
62+
using var client = new HttpClient();
63+
64+
using var requestBody = new MemoryStream();
65+
using (var writer = new StreamWriter(requestBody, leaveOpen: true))
8366
{
84-
HttpResponseMessage response;
67+
JsonSerializer.CreateDefault().Serialize(writer, parameters);
68+
}
8569

86-
using (var request = new MemoryStream())
87-
{
88-
using (var writer = new StreamWriter(request, leaveOpen: true))
89-
{
90-
JsonSerializer.CreateDefault().Serialize(writer, parameters);
91-
}
70+
HttpResponseMessage response;
9271

93-
request.Position = 0;
94-
using (var content = new StreamContent(request))
72+
requestBody.Position = 0;
73+
using (var request = new StreamContent(requestBody))
74+
{
75+
request.Headers.ContentType = new MediaTypeHeaderValue(contentType);
76+
if (headers != null)
77+
{
78+
foreach (var entry in headers)
9579
{
96-
content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
97-
if (headers != null)
98-
{
99-
foreach (var entry in headers)
100-
{
101-
content.Headers.Add(entry.Key, entry.Value);
102-
}
103-
}
104-
105-
response = await client.PostAsync(url, content).ConfigureAwait(false);
80+
request.Headers.Add(entry.Key, entry.Value);
10681
}
10782
}
10883

109-
response.EnsureSuccessStatusCode();
84+
response = await client.PostAsync(url, request).ConfigureAwait(false);
85+
}
86+
87+
var result = new HttpResponseMessage(response.StatusCode);
88+
foreach (var header in response.Headers)
89+
{
90+
result.Headers.Add(header.Key, header.Value);
91+
}
92+
93+
var responseBody = new MemoryStream();
94+
await using (var content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
95+
{
96+
await content.CopyToAsync(responseBody).ConfigureAwait(false);
97+
}
11098

111-
using (var content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
99+
responseBody.Position = 0;
100+
result.Content = new StreamContent(responseBody)
101+
{
102+
Headers =
112103
{
113-
return JsonSerializer.CreateDefault().Deserialize<T>(new JsonTextReader(new StreamReader(content)))!;
104+
ContentType = response.Content.Headers.ContentType
114105
}
115-
}
106+
};
107+
108+
return result;
116109
}
117110
}

Sources/ServiceModel.Grpc.AspNetCore/Internal/Swagger/SwaggerUiMiddleware.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ private async Task HandleRequestAsync(
110110
response = await proxy.GetResponseBody().ConfigureAwait(false);
111111
}
112112

113+
// non GRPC response => pass as is
114+
if (context.Response.StatusCode != (int)HttpStatusCode.OK
115+
|| !ProtocolConstants.MediaTypeNameGrpc.Equals(context.Response.Headers.ContentType, StringComparison.OrdinalIgnoreCase))
116+
{
117+
if (response.Length > 0)
118+
{
119+
await response.CopyToAsync(context.Response.BodyWriter.AsStream(true), context.RequestAborted).ConfigureAwait(false);
120+
}
121+
122+
return;
123+
}
124+
113125
context.Response.ContentType = ProtocolConstants.MediaTypeNameSwaggerResponse;
114126
if (status.StatusCode == StatusCode.OK)
115127
{

Sources/Versions.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<ServiceModelGrpcVersion>1.11.0</ServiceModelGrpcVersion>
3+
<ServiceModelGrpcVersion>1.11.1</ServiceModelGrpcVersion>
44
</PropertyGroup>
55
</Project>

0 commit comments

Comments
 (0)