Skip to content

Commit dd4a23e

Browse files
committed
Add ReuseScope to HttpResult to allow specifying a custom config scope when serializing a DTO response
1 parent dffd5cd commit dd4a23e

6 files changed

Lines changed: 79 additions & 37 deletions

File tree

src/ServiceStack.Interfaces/Web/IHttpResult.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//Copyright (c) Service Stack LLC. All Rights Reserved.
22
//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Net;
67

@@ -52,5 +53,10 @@ public interface IHttpResult : IHasOptions
5253
/// The padding length written with the body, to be added to ContentLength of body
5354
/// </summary>
5455
int PaddingLength { get; set; }
55-
}
56+
57+
/// <summary>
58+
/// Serialize the Response within the specified scope
59+
/// </summary>
60+
Func<IDisposable> ResultScope { get; set; }
61+
}
5662
}

src/ServiceStack/CompressedResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public object Response
4141

4242
public int PaddingLength { get; set; }
4343

44+
public Func<IDisposable> ResultScope { get; set; }
45+
4446
public IDictionary<string, string> Options
4547
{
4648
get { return this.Headers; }

src/ServiceStack/HttpError.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ public HttpStatusCode StatusCode
8686

8787
public int PaddingLength { get; set; }
8888

89+
public Func<IDisposable> ResultScope { get; set; }
90+
8991
public IDictionary<string, string> Options
9092
{
9193
get { return this.Headers; }

src/ServiceStack/HttpResponseExtensionsInternal.cs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,16 @@ public static Task<bool> WriteToResponse(this IResponse response, object result,
144144

145145
ApplyGlobalResponseHeaders(response);
146146

147+
IDisposable resultScope = null;
148+
147149
var httpResult = result as IHttpResult;
148150
if (httpResult != null)
149151
{
152+
if (httpResult.ResultScope != null)
153+
resultScope = httpResult.ResultScope();
154+
150155
if (httpResult.RequestContext == null)
151-
{
152156
httpResult.RequestContext = request;
153-
}
154157

155158
var paddingLength = bodyPrefix != null ? bodyPrefix.Length : 0;
156159
if (bodySuffix != null)
@@ -228,40 +231,47 @@ public static Task<bool> WriteToResponse(this IResponse response, object result,
228231
response.ContentType += ContentFormat.Utf8Suffix;
229232
}
230233

231-
var disposableResult = result as IDisposable;
232-
if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
234+
using (resultScope)
233235
{
234-
response.Flush(); //required for Compression
235-
if (disposableResult != null) disposableResult.Dispose();
236-
return TrueTask;
237-
}
236+
var disposableResult = result as IDisposable;
237+
if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
238+
{
239+
response.Flush(); //required for Compression
240+
if (disposableResult != null) disposableResult.Dispose();
241+
return TrueTask;
242+
}
238243

239-
if (httpResult != null)
240-
{
241-
result = httpResult.Response;
242-
}
244+
if (httpResult != null)
245+
result = httpResult.Response;
243246

244-
var responseText = result as string;
245-
if (responseText != null)
246-
{
247-
if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
248-
WriteTextToResponse(response, responseText, defaultContentType);
249-
if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
250-
return TrueTask;
251-
}
247+
var responseText = result as string;
248+
if (responseText != null)
249+
{
250+
if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
251+
WriteTextToResponse(response, responseText, defaultContentType);
252+
if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
253+
return TrueTask;
254+
}
252255

253-
if (defaultAction == null)
254-
{
255-
throw new ArgumentNullException("defaultAction", String.Format(
256-
"As result '{0}' is not a supported responseType, a defaultAction must be supplied",
257-
(result != null ? result.GetType().GetOperationName() : "")));
258-
}
256+
if (defaultAction == null)
257+
{
258+
throw new ArgumentNullException("defaultAction", String.Format(
259+
"As result '{0}' is not a supported responseType, a defaultAction must be supplied",
260+
(result != null ? result.GetType().GetOperationName() : "")));
261+
}
262+
263+
if (bodyPrefix != null)
264+
response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
259265

260-
if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
261-
if (result != null) defaultAction(request, result, response);
262-
if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
266+
if (result != null)
267+
defaultAction(request, result, response);
263268

264-
if (disposableResult != null) disposableResult.Dispose();
269+
if (bodySuffix != null)
270+
response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
271+
272+
if (disposableResult != null)
273+
disposableResult.Dispose();
274+
}
265275

266276
return FalseTask;
267277
}

src/ServiceStack/HttpResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ public HttpResult(byte[] responseBytes, string contentType)
107107

108108
public Dictionary<string, string> Headers { get; private set; }
109109

110+
public Func<IDisposable> ResultScope { get; set; }
111+
110112
private bool allowsPartialResponse;
111113
public bool AllowsPartialResponse
112114
{

tests/ServiceStack.WebHost.Endpoints.Tests/HttpResultTests.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using ServiceStack.Common.Tests;
44
using ServiceStack.Host;
55
using ServiceStack.Testing;
6+
using ServiceStack.Text;
67

78
namespace ServiceStack.WebHost.Endpoints.Tests
89
{
@@ -21,9 +22,9 @@ public void Can_send_ResponseText_test_with_Custom_Header()
2122
var httpResult = new HttpResult(customText, MimeTypes.Html)
2223
{
2324
Headers =
24-
{
25-
{"X-Custom","Header"}
26-
}
25+
{
26+
{"X-Custom","Header"}
27+
}
2728
};
2829

2930
var reponseWasAutoHandled = mockResponse.WriteToResponse(httpResult, MimeTypes.Html);
@@ -49,9 +50,9 @@ public void Can_send_ResponseStream_test_with_Custom_Header()
4950
var httpResult = new HttpResult(ms, MimeTypes.Html)
5051
{
5152
Headers =
52-
{
53-
{"X-Custom","Header"}
54-
}
53+
{
54+
{"X-Custom","Header"}
55+
}
5556
};
5657

5758
var reponseWasAutoHandled = mockResponse.WriteToResponse(httpResult, MimeTypes.Html);
@@ -97,6 +98,25 @@ public void Can_handle_null_HttpResult_StatusDescription()
9798

9899
Assert.IsNotNull(mockResponse.StatusDescription);
99100
}
101+
102+
[Test]
103+
public void Can_change_serialization_options()
104+
{
105+
var mockResponse = new MockHttpResponse();
106+
107+
var dto = new Poco();
108+
Assert.That(dto.ToJson(), Is.EqualTo("{}"));
109+
110+
var httpResult = new HttpResult(dto)
111+
{
112+
ResultScope = () => JsConfig.With(includeNullValues:true)
113+
};
114+
115+
var reponseWasAutoHandled = mockResponse.WriteToResponse(httpResult, MimeTypes.Html);
116+
Assert.That(reponseWasAutoHandled.Result, Is.True);
117+
118+
Assert.That(mockResponse.ReadAsString(), Is.EqualTo("{\"Text\":null}"));
119+
}
100120
}
101121

102122
}

0 commit comments

Comments
 (0)