Does DelegatingHandler has any performance side effects? - c#

I have this delegating handler in my api project:
class MyHandler : DelegatingHandler {
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken) {
var info = GrabSomeParametersFromHeader();
var isValid = await Validate(info); // this is a very light database query
if (!isValid) {
Log(request, info, false);
var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
return response;
}
HttpContext.Current.SetMobileRequestInfo(info);
var result = await base.SendAsync(request, cancellationToken);
Log(request, info, result.IsSuccessStatusCode &&
result.StatusCode == HttpStatusCode.OK);
return result;
}
}
HttpContext.Current.SetMobileRequestInfo simply adds the info object to http-items to use later in app.
The Validate is a very light database query. And the Log method is inserting request's data (such as URI and query-string etc.) to database.
Sometimes, I'm getting a weird behavior of the app: it simply goes down! There is no log, no error, nothing. Just server doesn't response the requests. I have to restart app (for example by making a fake change in web.config) to get it back to work. The app is a pretty simple app. I'm using ASP.NET Web API 2 on .NET 4.5 platform which is running on a IIS 7.5 machine. The only place I can think about is the mentioned delegating handler, which may cause the error. Do you have any idea? Does delegating handlers have any performance side effects which may cause the app to shut down?

The (very) short answer to your question is no. There is nothing inherent to DelegatingHandlers themselves that would cause your app to grind to a halt. It's far more likely that the issue is with what you're doing inside of it. Could be the database call, could be the logging. I'd recommend using a profiling tool to figure out where things are getting hung up.

Related

Exception when using SDK (HttpHeaders Collection was modified; enumeration operation may not execute.)

I'm having an issue with some code that I'm integration testing. I've searched other posts for issues relating to this exception, but in most of them it seems they're doing some manual editing of a collection when they shouldn't be.
I am working with an SDK written by someone else that encapsulates HttpClient. When instiantiating and setting up the SDK's client, I have to set the api key. The SDK provides a method for this on the client class. It looks like
public async Task SetApiKey(string apiKey)
{
await Task.Run(() =>
{
_apiKey = apiKey;
SetHeaders();
}).ConfigureAwait(false);
}
private void SetHeaders()
{
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Add("X-Api-Key", _apiKey);
_client.DefaultRequestHeaders.Add("X-Use-Raw", _useRawMode.ToString().ToLower());
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
I use this in my test and then attempt to use the client to retrieve something from the web api. This is an example of how it would be set up (_options contains the base url, etc).
var sdkClient = new SdkClient(_options);
await sdkClient.SetApiKey(apiKey);
var domainObject = await sdkClient.GetById(id);
GetById would contain some code like this. It would also map/serialize to the domain model, handle errors, etc.
var response = await _client.GetAsync(${BaseUrl}{Id}").ConfigureAwait(false);
Often, but not always, this throws an exception
Message: 
System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
Stack Trace: 
Enumerator.MoveNext()
HttpHeaders.AddHeaders(HttpHeaders sourceHeaders)
HttpRequestHeaders.AddHeaders(HttpHeaders sourceHeaders)
HttpClient.PrepareRequestMessage(HttpRequestMessage request)
HttpClient.CheckRequestBeforeSend(HttpRequestMessage request)
HttpClient.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
HttpClient.GetAsync(String requestUri)
Since it's not happening every time, I initially thought that a race condition was happening within the SDK, trying to set the headers while the request is being sent. However, I looked at the SDK code (that I've shared above) and am under the impression that "await Task.Run" should wait for the work in the background thread to be finished before the task is completed and SetApiKey returns. Then the client should be able to make the request no problem?
If there's something I'm lacking in my understanding of async await/threading, or if there's something else I'm missing - an explanation would be great, thanks.

OnActionExecutionAsync "result = new NotFoundObjectResult" does not change the result

I am trying to use ActionFilters to capture the result in my api call and change it if needed.
But it appears to change only the object i pass not the result type. (from OK to BadRequest)
return Ok(await _Data.Remove());
The above code in my controller leads to my filter:
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
Guid userId = await _data.GetId();
BaseModel baseModel = (BaseModel)((ObjectResult)resultContext.Result).Value;
if (baseModel.ApiResponse == ApiResponse.AllGood)
{
BaseModel m = _data.GetBaseModel(userId);
m.ApiResponse = baseModel.ApiResponse;
resultContext.Result = new OkObjectResult(m);
}
else if (baseModel.ApiResponse == ApiResponse.NotFound)
resultContext.Result = new NotFoundObjectResult(baseModel);
else
resultContext.Result = new BadRequestObjectResult(baseModel);
}
The model is correctly captured and i can check against its contents. And i can change it and pass it back. But it always goes back to the controller and then returns the "ok" with the new model.
What i want is it returning BadRequest
I have tried putting doing this:
resultContext.Result = new BadRequestObjectResult(baseModel);
As you can see from the above code, but it does not work.
The reason you cannot get it to work, is because this approach isn't possible (in the general case, at least).
The async variant of the ActionFilter combines both the pre and post variants of the synchronous variant, in one callback, by giving you next().
Once you have called and successfully returned from next(), you are in 'executed' land, not 'executing' land anymore.
The trouble is, if next() has started sending the response to the client, it is too late to change the response. Your receiving client may already sit with 'http 200 ok' in his hand!
You can observe this, by trying to write to the response in context.HttpContext.Response (You can set the Http Status Code on it, and use WriteAsync on it),
in the spot where you attempt to modify Result.
If next had already started the response, you'll get a direct exception telling you this.
However, IF next() hadn't begun the response, you may actually be able to control the response.
A last-resort option you have, is to throw at this point, it will (mostly) break your http response into http-500 server error. This may or may not be good for your client.
Lastly, your resultContext actually has a 'Cancelled' attribute which you can flag.
However I don't know if that has any effect if the response has already begun (it might affect the middleware server side).
In my own case for this, I concluded it was the wrong place to solve our issue, so I moved our necessary handling to a wrapper in the controller method return statement.
I.e. you then get
return wrapper(OK(response));
where wrapper will decide whether to pass on OK(..) unharmed.
If you use this approach, be careful if you had
return OK(responseWIthSideEffect(..));
in that case, you must make sure the side effect code is forced to execute,
before your wrapper makes its decision (i.e. assuming the wrapper checks something that might depend on that side effect).

Consuming WebAPI in blazor with authentication

I'm using the basic template that VS 2019 provides with the weather forecasting data when creating a ASP.NET WebAPI project and added some very basic authentication with user login and support for JWT Token which all works fine.
I'm trying to create a blazor client project to consume the API and display the data on the page. AFAIK Blazor doesn't support localstorage so I'm using Blazored LocalStorage package to give me this ability. My problem stems from fact using JS via OnInitializedAsync() is not possible in server-side blazor (https://github.com/aspnet/AspNetCore/issues/13396) as a result I'm not sure how one is suppose to consume these web api calls. As this will produce a null reference exception
protected override async Task OnInitializedAsync()
{
var client = HttpFactory.CreateClient();
var token = await LocalStorage.GetItemAsync<string>("authToken");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync("url/WeatherForecast");
var str = await response.Content.ReadAsStringAsync();
Items = JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(str);
}
One suggestion was to use OnAfterRenderAsync() method to call them as JS would be ready by then. Which semi-works but obviously the UI doesn't match because it needs to be refreshed - however to manually refresh it seems I have to call StateHasChanged(); which in turn calls OnAfterRender method again and as a result I had to put a check but this ultimately feels incredibly hacky.
private bool hasRendered;
protected override async Task OnAfterRenderAsync(bool _)
{
if (!hasRendered) return;
var client = HttpFactory.CreateClient();
var token = await LocalStorage.GetItemAsync<string>("authToken");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync("https://url/WeatherForecast");
var str = await response.Content.ReadAsStringAsync();
Items = JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(str);
StateHasChanged();
hasRendered = true;
}
What is the correct way to consume an API with authnetication and display the data correctly on the client side?
Side question HttpClient doesn't seem to be injectable in server-side and it's recommended to use HttpClientFactory - is it a good idea to create a client on every request or make a singleton and re-use thoughout the client project?
Q1
One suggestion was to use OnAfterRenderAsync() method to call them as JS would be ready by then. Which semi-works but obviously the UI doesn't match because it needs to be refreshed - however to manually refresh it seems I have to call StateHasChanged(); which in turn calls OnAfterRender method again and as a result I had to put a check but this ultimately feels incredibly hacky.
All people with the same issue, because this, at Lifecycle methods, new OnAfterRenderAsync with firstRender parm is documented:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await ... /// your auth code here.
}
}
Q2
Side question HttpClient doesn't seem to be injectable in server-side and it's recommended to use HttpClientFactory - is it a good idea to create a client on every request or make a singleton and re-use thoughout the client project?
Simplifying: I suggest to you to create two external libraries for your backend calls: one using http requests (for blazor wasm hosted model) and the other one just calling c# backend functions (for blazor server). Both with a common interface for backend calls. Use DI to set right library for each hosted model.

Using Google API GoogleJsonWebSignature.ValidateAsync(...) in server call

I am trying to implement Google SSO in my C# web application. It seemed to be pretty straightforward. Based on this tutorial, Google does its magic in the web browser to get an Id_Token JWT. I pass the token is passed it to my web service for validation and use it to match it to a user in the application.
Although I am not sure if my approach with the server-side Google API is correct yet, my big hangup is trying to figure out how to work with the async call in the Google API to see what I get back.
My code is pretty simple using the Google.Apis.Auth namespace:
public async Task<GoogleJsonWebSignature.Payload> wfValidateAsync(string pId_Token)
{
GoogleJsonWebSignature.Payload Sig = await GoogleJsonWebSignature.ValidateAsync(pId_Token, null, false);
return Sig;
}
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks. My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack. I think it means ending the service call before the async response finishes and the service can act on the result.
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
Thanks in advance to anyone who can help me screw my head back on straight with this.
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks.
But not use Typescript, right?
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
They should. Bad things can happen if you don't.
I think it means ending the service call before the async response finishes and the service can act on the result.
No! The compiler generates a state machine for methods with the async modifier and the await keyword means "go do something else and I'll come back here when I'm done".
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
You're probably making your test methods async void. They should be async Task in order for the test engine to know when the test is done.
Have a look at Stephen Cleary's blog. He has lots of content on async-await.
Paulo, Thank you!!
I was able to get this working with your advice on the last part about the test method. I had to change this:
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER
[TestMethod]
public async void AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
To this:
//THIS TEST METHOD SHOWS IN THE TEST EXPLORER
[TestMethod]
public async Task AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
NOW -- as an additional gotcha that was hanging me up, this will also cause a unit test to disappear from the test explorer, which I found out through lazy copy/pasting a non-test method's definition and mindlessly just adding a return when the build output told me I needed to return a value.
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER DUE TO RETURN VALUE
[TestMethod]
public async Task<bool> AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
return true;
}
In addition to the excellent blog you shared, this article from MSDN Magazine entitled Async Programming : Unit Testing Asynchronous Code helped me get my brain around it too.
What was hanging me up with all of this was mixing synchronous and async methods which, to your point, was not working well. The code seemed to skip the debug points I had set after the await calls as if it never ran them or, if it did run them, ran them somewhere I could not see and couldn't log to the debugger.
It was one of those terrible moments late on a Friday night where a developer starts to question both competence and sanity! :)
Thanks again for the help!
p.s. I incorrectly typed Angular JS in my question and meant Angular 4...and I am using TypeScript there. Some day I'll stop incorrectly referring the newer versions of Angular as AngularJS.
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
It's not entirely true. You can use async method in sync methods. Of course you are losing most of the 'async' effect, but sometimes you have to. As async methods returs tasks you have some options.
When Task is returning result, any reference to t.Result will block execution till it's known. Another option is to use Task.Wait(preferably with timeout). For example code to validate google jwt token:
public bool ValidateGoogleToken(string token)
{
try
{
if(GoogleJsonWebSignature.ValidateAsync(token).Wait(1000))
return true; //success
//timeout exceeded
}
catch (Exception e)
{
//tampered token
}
return false;
}

Can I run WCF on top of WebAPI?

For a work project, I'm building an application consisting of a frontend SPA (Aurelia), a WebAPI backend for that SPA, and a multitude of web service WebAPI projects contained in existing applications. (This application will perform data aggregation - it's a dashboard for our clients to show relevant information from many sources.)
Immediately, I was faced with some challenges. Using WebAPI, we wanted to expose the web services as REST endpoints. This works well for client applications and is very open. However, making server-to-server calls in .NET, I wanted to abstract away the REST calls and simply provide a method-based interface (so I could call, say, new MyWebServiceClient().getOrders() or something like that); I also did not want to have to duplicate data model classes across solutions, or worry about deserializing one JSON model type to another type. (Blegh.)
To achieve this goal, I've created an internal nuget package that a) provides access to the data model classes used in the service via the assembly, and b) provides an interface for HTTP calls, abstracting away the JSON serialization and deserialization, like so:
public async Task<T> Get<T>(string endpoint, IEnumerable<KeyValuePair<string, string>> parameters = null, CancellationToken cancellationToken = default(CancellationToken))
{
var builder = new UriBuilder(Properties.Settings.Default.MyEndpointHost + endpoint);
builder.Query = buildQueryStringFromParameters(parameters);
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// After this, we really shouldn't continue.
var request = await _httpClient.GetAsync(builder.Uri, cancellationToken);
if (!request.IsSuccessStatusCode)
{
if (request.StatusCode >= HttpStatusCode.BadRequest && request.StatusCode < HttpStatusCode.InternalServerError)
{
throw new EndpointClientException("Service responded with an error message.", request.StatusCode, request.ReasonPhrase);
}
if (request.StatusCode >= HttpStatusCode.InternalServerError && (int)request.StatusCode < 600)
{
throw new EndpointServerException("An error occurred in the Service endpoint.", request.StatusCode, request.ReasonPhrase);
}
}
var json = await request.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(json);
}
catch (Exception ex)
{
throw;
}
}
The public methods are simply convenience methods, calling that function with the requisite arguments, like this:
public async Task<IEnumerable<MyModel>> SearchMyModelsByFooName(string fooName, CancellationToken cancellationToken)
{
var parameters = new List<KeyValuePair<string, string>>();
parameters.Add(new KeyValuePair<string, string>("searchText", fooName));
return await this.Get<List<MyModel>>("myModel", parameters, cancellationToken);
}
I've had good results with this, although I have to maintain it manually and update dependencies.
However, upon talking to my colleagues, I was introduced to WCF, and it looks as though it solves a lot of the issues I'm trying to solve manually. Looking into this, though, reveals that the setup can be tricky, and we're not sure if it's worth the trouble. (Additionally, we'd have to maintain two APIs.)
Although it's fun, I don't want to reinvent the wheel. Is there a way to bolt WCF on top of WebAPI for server-to-server calls only, or have WCF generate data for WebAPI controllers?
If you don't need to use REST, and personally I don't see any reason to do this in a .NET server to server scenario, you could just create a WCF service to expose your data. Then generate proxy classes in your WebAPI that calls the WCF service.
Using svutil to generate the proxy classes make it easy to adapt to changes in WCF "API". And you will have, as you call it, a method based interface to interact with the WCF service/s.
Reference for using svutil:
https://msdn.microsoft.com/en-us/library/ms733133(v=vs.110).aspx

Categories

Resources