Skip to content

Commit 27ea2f1

Browse files
author
Tsar Nikolay
committed
Fix #2294: batch processing loses HttpContext for main request.
1 parent 97e05e8 commit 27ea2f1

File tree

2 files changed

+97
-14
lines changed

2 files changed

+97
-14
lines changed

src/Microsoft.AspNetCore.OData/Batch/ODataBatchReaderExtensions.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Microsoft.AspNet.OData.Interfaces;
1515
using Microsoft.AspNetCore.Http;
1616
using Microsoft.AspNetCore.Http.Features;
17-
using Microsoft.Extensions.DependencyInjection;
1817
using Microsoft.Extensions.Primitives;
1918
using Microsoft.OData;
2019

@@ -239,17 +238,9 @@ private static HttpContext CreateHttpContext(HttpContext originalContext)
239238

240239
features[typeof(IHttpResponseFeature)] = new HttpResponseFeature();
241240

242-
// Create a context from the factory or use the default context.
243-
HttpContext context = null;
244-
IHttpContextFactory httpContextFactory = originalContext.RequestServices.GetRequiredService<IHttpContextFactory>();
245-
if (httpContextFactory != null)
246-
{
247-
context = httpContextFactory.Create(features);
248-
}
249-
else
250-
{
251-
context = new DefaultHttpContext(features);
252-
}
241+
// Create a context.
242+
// IHttpContextFactory should not be used, because it resets IHttpContextAccessor.HttpContext;
243+
HttpContext context = new DefaultHttpContext(features);
253244

254245
// Clone parts of the request. All other parts of the request will be
255246
// populated during batch processing.

test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Batch/DefaultODataBatchHandlerTest.cs

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
using Microsoft.AspNet.OData.Extensions;
1313
using Microsoft.AspNet.OData.Test.Abstraction;
1414
using Microsoft.AspNet.OData.Test.Common;
15+
using Microsoft.Extensions.DependencyInjection;
16+
using Microsoft.Extensions.DependencyInjection.Extensions;
1517
using Xunit;
1618
#if !NETCORE
1719
using System.Web.Http;
1820
using System.Web.Http.Routing;
1921
#else
22+
using Microsoft.AspNetCore.Http;
2023
using Microsoft.AspNetCore.Mvc;
2124
using Newtonsoft.Json;
2225
#endif
@@ -727,7 +730,7 @@ public async Task SendAsync_CorrectlyHandlesCookieHeader()
727730
var changesetRef = $"changeset_{Guid.NewGuid()}";
728731
var endpoint = "http://localhost";
729732

730-
Type[] controllers = new[] { typeof(BatchTestCustomersController), typeof(BatchTestOrdersController), };
733+
Type[] controllers = new[] { typeof(BatchTestOrdersController), };
731734
var server = TestServerFactory.Create(controllers, (config) =>
732735
{
733736
var builder = ODataConventionModelBuilderFactory.Create(config);
@@ -782,8 +785,73 @@ public async Task SendAsync_CorrectlyHandlesCookieHeader()
782785
var response = await client.SendAsync(batchRequest);
783786

784787
ExceptionAssert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
788+
}
789+
790+
[Fact]
791+
public async Task ProcessBatchAsync_PreservesHttpContext()
792+
{
793+
var batchRef = $"batch_{Guid.NewGuid()}";
794+
var changesetRef = $"changeset_{Guid.NewGuid()}";
795+
var endpoint = "http://localhost";
796+
797+
Type[] controllers = new[] { typeof(BatchTestOrdersController), };
798+
var server = TestServerFactory.Create(
799+
controllers,
800+
config =>
801+
{
802+
var builder = ODataConventionModelBuilderFactory.Create(config);
803+
builder.EntitySet<BatchTestOrder>("BatchTestOrders");
804+
805+
config.MapODataServiceRoute("odata", null, builder.GetEdmModel(), new CustomODataBatchHandler());
806+
config.Expand();
807+
config.EnableDependencyInjection();
808+
},
809+
config =>
810+
{
811+
config.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
812+
});
813+
814+
var client = TestServerFactory.CreateClient(server);
815+
816+
var orderId = 2;
817+
var createOrderPayload = $@"{{""@odata.type"":""Microsoft.AspNet.OData.Test.Batch.BatchTestOrder"",""Id"":{orderId},""Amount"":50}}";
818+
819+
var batchRequest = new HttpRequestMessage(HttpMethod.Post, $"{endpoint}/$batch");
820+
batchRequest.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("text/plain"));
821+
822+
var batchContent = $@"
823+
--{batchRef}
824+
Content-Type: multipart/mixed;boundary={changesetRef}
825+
826+
--{changesetRef}
827+
Content-Type: application/http
828+
Content-Transfer-Encoding: binary
829+
Content-ID: 1
830+
831+
POST {endpoint}/BatchTestOrders HTTP/1.1
832+
Content-Type: application/json;type=entry
833+
Prefer: return=representation
785834
786-
// TODO: assert somehow?
835+
{createOrderPayload}
836+
--{changesetRef}--
837+
--{batchRef}
838+
Content-Type: application/http
839+
Content-Transfer-Encoding: binary
840+
841+
GET {endpoint}/BatchTestOrders({orderId}) HTTP/1.1
842+
Content-Type: application/json;type=entry
843+
Prefer: return=representation
844+
845+
--{batchRef}--
846+
";
847+
848+
var httpContent = new StringContent(batchContent);
849+
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/mixed;boundary={batchRef}");
850+
httpContent.Headers.ContentLength = batchContent.Length;
851+
batchRequest.Content = httpContent;
852+
var response = await client.SendAsync(batchRequest);
853+
854+
ExceptionAssert.DoesNotThrow(() => response.EnsureSuccessStatusCode());
787855
}
788856
#endif
789857
}
@@ -824,6 +892,13 @@ public class BatchTestOrder
824892
return new List<BatchTestOrder> { order01 };
825893
});
826894

895+
896+
[EnableQuery]
897+
public SingleResult<BatchTestOrder> Get([FromODataUri]int key)
898+
{
899+
return SingleResult.Create(Orders.Where(d => d.Id.Equals(key)).AsQueryable());
900+
}
901+
827902
public static IList<BatchTestOrder> Orders
828903
{
829904
get
@@ -927,5 +1002,22 @@ public class BatchTestHeadersCustomer
9271002
{
9281003
public int Id { get; set; }
9291004
}
1005+
1006+
public class CustomODataBatchHandler : DefaultODataBatchHandler
1007+
{
1008+
/// <inheritdoc />
1009+
public override async Task ProcessBatchAsync(HttpContext context, RequestDelegate nextHandler)
1010+
{
1011+
// Retrieve current httpcontext.
1012+
var httpContextAccessor = context.RequestServices.GetService<IHttpContextAccessor>();
1013+
var beforeContext = httpContextAccessor?.HttpContext;
1014+
await base.ProcessBatchAsync(context, nextHandler);
1015+
var afterContext = httpContextAccessor?.HttpContext;
1016+
if (httpContextAccessor != null)
1017+
{
1018+
Assert.Equal(beforeContext, afterContext);
1019+
}
1020+
}
1021+
}
9301022
#endif
9311023
}

0 commit comments

Comments
 (0)