Preserve HttpContext when going async with WebAPI (Medium Trust) - asp.net

I am building a set of ASP.Net hosted WebAPI services that must use an old library which depends heavily on HttpContext.Current. I am having trouble ensuring that context is preserved in all the methods that participate in an async call. I have tried several variations with await/Task.Wait and TaskScheduler.FromCurrentSynchronizationContext() on the below code.
[HttpGet]
public Task<IEnumerable<string>> ContinueWith()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); //or another culture that is not the default on your machine
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var output = new List<string> { TestOutput("Action start") };
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
return TestOutput("In Task");
}).ContinueWith(slowString =>
{
output.Add(slowString.Result);
output.Add(TestOutput("Action end"));
return output as IEnumerable<string>;
});
output.Add(TestOutput("Action Mid"));
return task;
}
private string TestOutput(string label)
{
var s = label + " ThreadID: " + Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture);
s += " " + Thread.CurrentThread.CurrentCulture.EnglishName;
s += HttpContext.Current == null ? " No Context" : " Has Context";
Debug.WriteLine(s);
return s;
}
I would like to be able to ensure that the CurrentCulture is fr-FR, and that HttpContext.Current is not null at each point where TestOutput is called. I have not succeeded in doing that for the "In Task" call with anything I have tried. Also in some of my test thread id never varies suggesting that I have effectively removed the asynchronicity of the method. How can I ensure that the culture and HttpContext.Current are preserved at each call to TestOutput, and that the code is free to run on different threads?
Capturing HttpContext.Current in a closure and then simply setting it again will not work for me as I need to support Medium Trust which will throw a security exception when calling the HttpContext.Current setter.

A little noticed fact, HttpContext.Current is writable.
var context = HttpContext.Current;
var task = Task.Factory.StartNew(() => {
HttpContext.Current = context;
// You may want to set CultureInformation here too.
return TestOutput("In Task");
});

Context is preserved whenever you await tasks.
What you're seeing is that there's no context for thread pool tasks (Task.Run, TaskFactory.StartNew, or for that matter BackgroundWorker or Thread or Delegate.BeginInvoke). This is normal and expected.
So, don't use a thread pool task. Your example code seems to want to do parallel processing with multiple threads having the HttpContext, which simply isn't possible.
You can do concurrent async methods if you want, but this requires that your Thread.Sleep can actually be an async method instead of a CPU-based method:
[HttpGet]
public async Task<IEnumerable<string>> Test()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var output = new List<string> { TestOutput("Action start") };
var task = SlowStringAsync();
output.Add(TestOutput("Action Mid"));
output.Add(await task);
output.Add(TestOutput("Action end"));
return output;
}
public async Task<string> SlowStringAsync()
{
await Task.Delay(1000);
return TestOutput("In Task");
}
If your old library is out of your control and you can't make it async, then you'll have to call it synchronously. It's acceptable to call a synchronous method from an async method in situations like this:
[HttpGet]
public async Task<IEnumerable<string>> Test()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var output = new List<string> { TestOutput("Action start") };
output.Add(TestOutput("Action Mid"));
Thread.Sleep(1000);
output.Add(TestOutput("Not Really In Task"));
output.Add(TestOutput("Action end"));
return output;
}

Related

Implement Redis Cache in place of In Memory Cache?

I have a InMemory Cache method which I want to implement using Redis Cache, it uses the concept of Semaphore in the InMemory Cache, hence I have gone through many documentation's like the Redis documentation on Distributed Locking, and tried using different locking mechanisms in Redis such as RedLock.Net , RedLock-cs etc, but I haven't been able to completely go through with the technique, as even though I am able to recreate the method, but the running time for large number of processes for it in Redis is coming out to be huge.
The code that I am trying to recreate is as follows:
private readonly ConcurrentDictionary<string, Lazy<SemaphoreSlim>> _semaphores = new ConcurrentDictionary<string, Lazy<SemaphoreSlim>>();
public async ValueTask<TValue> GetMethod<TValue>(string key,Func<Task<TValue>> get,int time)
{
return await SemaphoreMethod(key, async() =>
{
var value = await get();
if(value != null)
cache.set(key,value,time);
return value;
});
}
private async ValueTask<TValue> SemaphoreMethod<TValue>(string key,Func<Task<TValue>> get)
{
if(cache.TryGetValue(key,out TValue result))
return result;
var semaphore = _semaphores.GetOrAdd(key,k => new Lazy<SemaphoreSlim>(() => new SemaphoreSlim(1,1))).Value;
await semaphore.WaitAsync();
TValue answer = default;
if(cache.TryGetValue(key,out TValue result2))
return result2;
answer = await get();
_semaphores.TryRemove(key,out _);
semaphore.Release();
return answer;
}
And the method which I run against it to check it's implementation and efficiency in comparison to InMemory Cache is as follows:
public async Task GetValueAsync_Tasks()
{
var callCount = 0;
async Task<string> Get()
{
Interlocked.Increment(ref callCount);
return "value";
}
const int count = 1000;
var valueTasks = new List<ValueTask<string>>(count);
for(var i=0;i<count;i++)
valueTask.Add(GetMethod("key",Get));
var tasks = valueTasks.Select(vt => vt.AsTask()).ToArray();
Task.WaitAll(tasks);
callCount.Should().Be(1);
}
One other thing that I am not able to understand that in which part of the test method, would the code run all the tasks created, i.e. in this one
valueTasks.Add(sut.GetValueAsync("key", FetchValue));
or in this one
Task.WaitAll(tasks);
I am using the StackExchange.Redis library, and Azure Cache for Redis for storing the respective data.

How to access HttpContext inside a unit test in ASP.NET 5 / MVC 6

Lets say I am setting a value on the http context in my middleware. For example HttpContext.User.
How can test the http context in my unit test. Here is an example of what I am trying to do
Middleware
public class MyAuthMiddleware
{
private readonly RequestDelegate _next;
public MyAuthMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.User = SetUser();
await next(context);
}
}
Test
[Fact]
public async Task UserShouldBeAuthenticated()
{
var server = TestServer.Create((app) =>
{
app.UseMiddleware<MyAuthMiddleware>();
});
using(server)
{
var response = await server.CreateClient().GetAsync("/");
// After calling the middleware I want to assert that
// the user in the HttpContext was set correctly
// but how can I access the HttpContext here?
}
}
Following are two approaches you could use:
// Directly test the middleware itself without setting up the pipeline
[Fact]
public async Task Approach1()
{
// Arrange
var httpContext = new DefaultHttpContext();
var authMiddleware = new MyAuthMiddleware(next: (innerHttpContext) => Task.FromResult(0));
// Act
await authMiddleware.Invoke(httpContext);
// Assert
// Note that the User property on DefaultHttpContext is never null and so do
// specific checks for the contents of the principal (ex: claims)
Assert.NotNull(httpContext.User);
var claims = httpContext.User.Claims;
//todo: verify the claims
}
[Fact]
public async Task Approach2()
{
// Arrange
var server = TestServer.Create((app) =>
{
app.UseMiddleware<MyAuthMiddleware>();
app.Run(async (httpContext) =>
{
if(httpContext.User != null)
{
await httpContext.Response.WriteAsync("Claims: "
+ string.Join(
",",
httpContext.User.Claims.Select(claim => string.Format("{0}:{1}", claim.Type, claim.Value))));
}
});
});
using (server)
{
// Act
var response = await server.CreateClient().GetAsync("/");
// Assert
var actual = await response.Content.ReadAsStringAsync();
Assert.Equal("Claims: ClaimType1:ClaimType1-value", actual);
}
}
The RC1 version of asp.net 5/MVC6 makes it possible to set HttpContext manually in Unit Tests, which is awesome!
DemoController demoController = new DemoController();
demoController.ActionContext = new ActionContext();
demoController.ActionContext.HttpContext = new DefaultHttpContext();
demoController.HttpContext.Session = new DummySession();
DefaultHttpContext class is provided by the platform.
DummySession can be just simple class that implements ISession class. This simplifies things a lot, because no more mocking is required.
It would be better if you unit test your middleware class in isolation from the rest of your code.
Since HttpContext class is an abstract class, you can use a mocking framework like Moq (adding "Moq": "4.2.1502.911", as a dependency to your project.json file) to verify that the user property was set.
For example you can write the following test that verifies your middleware Invoke function is setting the User property in the httpContext and calling the next middleware:
[Fact]
public void MyAuthMiddleware_SetsUserAndCallsNextDelegate()
{
//Arrange
var httpContextMock = new Mock<HttpContext>()
.SetupAllProperties();
var delegateMock = new Mock<RequestDelegate>();
var sut = new MyAuthMiddleware(delegateMock.Object);
//Act
sut.Invoke(httpContextMock.Object).Wait();
//Assert
httpContextMock.VerifySet(c => c.User = It.IsAny<ClaimsPrincipal>(), Times.Once);
delegateMock.Verify(next => next(httpContextMock.Object), Times.Once);
}
You could then write additional tests for verifying the user has the expected values, since you will be able to get the setted User object with httpContextMock.Object.User:
Assert.NotNull(httpContextMock.Object.User);
//additional validation, like user claims, id, name, roles
take a look at this post:
Setting HttpContext.Current.Session in a unit test
I think what you need is this.
public static HttpContext FakeHttpContext(string url)
{
var uri = new Uri(url);
var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
return httpContext;
}
Then you can use it like:
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
HttpContextFactory.Current.Request.Headers.Add(key, value);

Dependency Injection to Handle Case When HttpContext.Current Doesn't Exist

I have an ASP.NET MVC application and in my Application_Start event I have the following code:
container.RegisterType<ISessionFactory>(new ContainerControlledLifetimeManager(), new InjectionFactory(c => {
return BuildSessionFactory();
}));
container.RegisterType<ISession>(new PerRequestLifetimeManager<ISession>(), new InjectionFactory(c => {
return c.Resolve<ISessionFactory>().OpenSession();
}));
The session factory (ISessionFactory) lives for the length of the application. The session (ISession) lives for the length of the ASP.NET request. I also dispose the session in the Application_EndRequest event. This allows me to inject the ISession throughout my application and it works as expected.
I'm now trying to build task scheduling into my application. I have added the following code into the Application_Start event:
var timer = new System.Timers.Timer(5000);
timer.Elapsed += (sender, e) => {
var thread = new Thread(new ThreadStart(() => {
var service = DependencyResolver.Current.GetService<ISomeService>();
...
}));
thread.Start();
};
timer.Enabled = true;
This should run every 5 seconds. The implementation of ISomeService injects the ISession in the constructor and I don't wish to change this class. My issue arises when it tries to resolve ISession as it tries to resolve it in a thread where HttpContext.Current is null and therefore an exception is thrown. I was wondering how I should register the session to handle this scenario. I'd appreciate the help.
Thanks
Here's my PerRequestLifetimeManager class incase it helps:
public class PerRequestLifetimeManager<T> : LifetimeManager {
public override object GetValue() {
return HttpContext.Current.Items[typeof(T).AssemblyQualifiedName];
}
public override void RemoveValue() {
HttpContext.Current.Items.Remove(typeof(T).AssemblyQualifiedName);
}
public override void SetValue(object newValue) {
HttpContext.Current.Items[typeof(T).AssemblyQualifiedName] = newValue;
}
}
Resolve the ISessionFactory and manage the lifetime of the session yourself.
var timer = new System.Timers.Timer(5000);
timer.Elapsed += (sender, e) => {
var thread = new Thread(new ThreadStart(() => {
var service = DependencyResolver.Current.GetService<ISessionFactory>();
using(var session = service.OpenSession())
{
//do something with session
}
...
}));
thread.Start();
};
timer.Enabled = true;
Edit:
Unity has a feature with multiple container instances that can have different configuration. That way you can have a different lifetimemanager configured for the "service"

Localization for async calls in ASP.NET WebApi

Hi– I'm planning to handle server side localization for error strings etc. based on the “Accept-Language” header, by setting the CurrentUICulture based on that header, but apparently it doesn’t flow though the async calls, below is a sample code to illustrate the problem, is there any default way of handling the localization for async calls?
public async Task<HttpResponseMessage> GetAsync()
{
//set the current ui culture, to say "fr-FR", based on "Accept-Language" header
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("**fr-FR**");
var task = httpClient.PutAsync(endpoint, content)
//do some processing
var res = await task;
var culture = Thread.CurrentThread.CurrentUICulture.Name; **//ITS NOT necessarily fr-FR**
//do some more processing
//and handle localizations etc.
return res;
}
I'm looking for a cleaner/seamless way of handling localization for cases where there are real async operations esp. for the code following the await call
Edit: replaced Task.Run() with httpClient.PutAsync for clarity
Task.Run and Task.Factory.StartNew do not have any context. That's an expected behavior. Context is preserved when you use the await keyword. So here's what you could do:
public static async Task<IEnumerable<string>> GetAsync()
{
//set the current ui culture, to say "fr-FR", based on "Accept-Language" header
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr-FR");
//do some processing
var s = await GetSomething();
var culture = Thread.CurrentThread.CurrentUICulture.Name; //It's ja-JP
return new[] { s, s };
}
public static Task<string> GetSomething()
{
var cuture = Thread.CurrentThread.CurrentUICulture.Name; // It's fr-FR
var tcs = new TaskCompletionSource<string>();
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("ja-JP");
tcs.SetResult("<something>");
return tcs.Task;
}

Async calls in WP7

I have been experimenting with WP7 apps today and have hit a bit of a wall.
I like to have seperation between the UI and the main app code but Ive hit a wall.
I have succesfully implemented a webclient request and gotten a result, but because the call is async I dont know how to pass this backup to the UI level. I cannot seem to hack in a wait for response to complete or anything.
I must be doing something wrong.
(this is the xbox360Voice library that I have for download on my website: http://www.jamesstuddart.co.uk/Projects/ASP.Net/Xbox_Feeds/ which I am porting to WP7 as a test)
here is the backend code snippet:
internal const string BaseUrlFormat = "http://www.360voice.com/api/gamertag-profile.asp?tag={0}";
internal static string ResponseXml { get; set; }
internal static WebClient Client = new WebClient();
public static XboxGamer? GetGamer(string gamerTag)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null);
return SerializeResponse(response);
}
internal static XboxGamer? SerializeResponse(string response)
{
if (string.IsNullOrEmpty(response))
{
return null;
}
var tempGamer = new XboxGamer();
var gamer = (XboxGamer)SerializationMethods.Deserialize(tempGamer, response);
return gamer;
}
internal static string GetResponse(string url, string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
Client.Credentials = new NetworkCredential(userName, password);
}
try
{
Client.DownloadStringCompleted += ClientDownloadStringCompleted;
Client.DownloadStringAsync(new Uri(url));
return ResponseXml;
}
catch (Exception ex)
{
return null;
}
}
internal static void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
ResponseXml = e.Result;
}
}
and this is the front end code:
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
var xboxGamer = xboxManager.GetGamer();
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
Now I understand I need to place something in ClientDownloadStringCompleted but I am unsure what.
The problem you have is that as soon as an asynchronous operation is introduced in to the code path the entire code path needs to become asynchronous.
Because GetResponse calls DownloadStringAsync it must become asynchronous, it can't return a string, it can only do that on a callback
Because GetGamer calls GetResponse which is now asynchronous it can't return a XboxGamer, it can only do that on a callback
Because GetGamerDetails calls GetGamer which is now asynchronous it can't continue with its code following the call, it can only do that after it has received a call back from GetGamer.
Because GetGamerDetails is now asynchronous anything call it must also acknowledge this behaviour.
.... this continues all the way up to the top of the chain where a user event will have occured.
Here is some air code that knocks some asynchronicity in to the code.
public static void GetGamer(string gamerTag, Action<XboxGamer?> completed)
{
var url = string.Format(BaseUrlFormat, gamerTag);
var response = GetResponse(url, null, null, (response) =>
{
completed(SerializeResponse(response));
});
}
internal static string GetResponse(string url, string userName, string password, Action<string> completed)
{
WebClient client = new WebClient();
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
client.Credentials = new NetworkCredential(userName, password);
}
try
{
client.DownloadStringCompleted += (s, args) =>
{
// Messy error handling needed here, out of scope
completed(args.Result);
};
client.DownloadStringAsync(new Uri(url));
}
catch
{
completed(null);
}
}
public void GetGamerDetails()
{
var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");
xboxManager.GetGamer( (xboxGamer) =>
{
// Need to move to the main UI thread.
Dispatcher.BeginInvoke(new Action<XboxGamer?>(DisplayGamerDetails), xboxGamer);
});
}
void DisplayGamerDetails(XboxGamer? xboxGamer)
{
if (xboxGamer.HasValue)
{
var profile = xboxGamer.Value.Profile[0];
imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));
txtUserName.Text = profile.GamerTag;
txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");
txtZone.Text = profile.PlayerZone;
}
else
{
txtUserName.Text = "Failed to load data";
}
}
As you can see async programming can get realy messy.
You generally have 2 options. Either you expose your backend code as an async API as well, or you need to wait for the call to complete in GetResponse.
Doing it the async way would mean starting the process one place, then return, and have the UI update when data is available. This is generally the preferred way, since calling a blocking method on the UI thread will make your app seem unresponsive as long as the method is running.
I think the "Silverlight Way" would be to use databinding. Your XboxGamer object should implement the INotifyPropertyChanged interface. When you call GetGamer() it returns immediately with an "empty" XboxGamer object (maybe with GamerTag=="Loading..." or something). In your ClientDownloadStringCompleted handler you should deserialize the returned XML and then fire the INotifyPropertyChanged.PropertyChanged event.
If you look at the "Windows Phone Databound Application" project template in the SDK, the ItemViewModel class is implemented this way.
Here is how you can expose asynchronous features to any type on WP7.

Resources