I have a .NET core dependency that is scoped to each REST API request. It is added in Startup.ConfigureServices with a call to AddScoped.
I want to add cancellation support to this. If I add a CancellationToken cancellationToken to any controller action's parameters I can get a token that is cancelled if the client-side request is. I can then pass that token to all the methods on my dependency.
However, the dependency is scoped to the request, so passing the token down through the action to the methods feels unnecessary - could I just add the CancellationToken to the scoped dependency somehow?
could I just add the CancellationToken to the scoped dependency somehow?
Well, technically yes. i.e. by injecting IHttpAccessorand accessing HttpContext.RequestAborted property, which is the same cancellation token you usually get passed into the controllers action if defined.
But using the action parameter overload is actually kinda discouraged as in every controller action you can access the cancellation token via HttpContext.RequestAborted and having it in controllers action kinda makes the token public, i.e. when creating Swagger scheme (at least was the case back in 2017), where as using the HttpContext itself didn't expose it to the public.
The only exception to that seems to be, when using "Poco Controllers" which don't inherit from Controller or ControllerBase and not injecting IHttpAccessor into this controller.
But injecting cancellation tokens into arbitrary services is problematic as you get a hard dependency on the web framework (IHttpAccessor/HttpContext).
It's best and cleanest to keep having a CancellationToken parameter on your methods which can be cancelled in a meaningful way and make the token optional, so you can only pass the parameter in situation where you have a request or a situation that can be cancelled
public Task<Result> ProcessSomething(string param1, CancellationToken cancellationToken = default)
{
}
Related
We are using NSwag generated client code for a web API. The API requires an Authorization header to be set on all requests.
This header value needs to be generated from async library methods (ITokenAcquisition.GetAccessTokenForUserAsync() in case it matters).
So far the best option we've come up with is to create a class that implements
public Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
HttpCompletionOption completionOption,
CancellationToken cancellationToken)
wrapping an HTTPClient, which would allow us to get the token and set the header before calling SendAsync on the wrapped HttpClient. We can then inject that class as the HttpClient used by the NSwag code.
I don't think we can use NSwag's CreateHttpClientAsync because I can't see how to inject the ITokenAcquisition (and other dependencies) into the base class. (Unless the client generation code is cleverer than I'm giving it credit for)
Have we missed something?
The best solution we came up with doesn't actually use any NSwag customisation in the end.
Instead we added a class inheriting from DelegatingHandler and overrode SendAsync there.
We then added our DelegatingHandler to the HttpClient passed to the NSwag client. e.g. during service registration:
services.AddHttpClient<NSwagClientInterface, NSwagClientImplementation>((provider, client) =>
{
client.BaseAddress = new Uri(Configuration["BaseAddress"]);
})
.AddHttpMessageHandler<MyDelegatingHandler>();
On my controllers actions, I can insert a CancellationToken parameter and the platform will fill it so I can implement cooperative cancellation. How can I configure this token? I would like to set its timeout to 10sec for example (pref in a global middleware)
What I am trying to achieve - My application is simply ASP .Net Core application. It is not Web API. I want to execute method before every post/get request from my app to external sources, for example:
I am sending a post request, to check SSL expiry date to some website API and it returns me a response. According to the response I am sending another request or not. I don't want to place call method statement before every request, I would like to do it globally.
I was trying to achieve this based on http://www.sulhome.com/blog/10/log-asp-net-core-request-and-response-using-middleware
As it occurs, this middleware works(I have it working) only for internal requests(routing requests inside application).
Is there any possibility to do it for all requests?
Thanks in advance
.NET Core allows to create custom middlewares to get into MV pipeline. Here is an example:
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//do your checkings
await _next(context);
}
}
In Startup.cs in Config method just regiester it:
app.UseMiddleware<MyMiddleware>(Options.Create(options));
Since you are talking about doing a call on Outgoing requests, you have two mechanisms to solve the problem:
Use an Aspect Oriented Programming Library (like https://www.postsharp.net)
Implement your own Request class (that has the global behavior you desire) and make sure that all requests are done using this class (or a class that inherits from it).
For the second point, a good mechanism is that the base class provides a SendRequest method that receives an HttpRequestMessage and executes the global code. Classes that inherit from it use this method to send the requests and have no access to the underlying HttpClient (so that they cannot run around the SendRequest method).
I am trying to access the Request property in my ApiController-derived class.
For some reason, Request is null in ExecuteAsync method. I've seen the other questions, so before you ask:
I am not initializing the controller by calling the constructor, it's a regular HTTP POST API call from an external device.
I've tried the same request locally with Fiddler, the behavior is identical.
I am not unit testing.
Before hitting the ExecuteAsync method, my request passes through a delegating handler, and in the delegating handler, my request object exists (I even add some properties without any problem).
At the last line of delegating handler, I call return await base.SendAsync(request, cancellationToken); without a problem and request exists.
Right after than in API controller, HttpContext.Current.Request is not null and accessible without problem.
In API controller, RequestContext is not null and accessible without problem.
In that same line, Request is null.
Why would this occur? I'm on Web API 2.2 (and MVC 5, if relevant).
This is probably due to the fact that you're trying to access HttpContext while working with async/await.
So, you have two options:
Access the request via the ExecuteAsync method 'HttpControllerContext' parameter - controllerContext.Request.
Make sure your web.config is targeting .NET 4.5 and update appSettings with aspnet:UseTaskFriendlySynchronizationContext set to true.
You can read more here - Using HttpContext Safely After Async in ASP.NET MVC Applications.
To better understand what's going on under the hood, I'd recommend:
Understand what is SynchronizationContext - ExecutionContext vs SynchronizationContext
Understand how it is related to ASP.NET - Understanding the SynchronizationContext in ASP.NET.
In a very very high level: in a synchronous server implementations, where the entire request is processed by the same thread, the execution context is stored using TLS (thread local storage), means HttpContext is available anywhere in your code.
In an asynchronous server implementation (async/await), the request may be processed by several threads, and there's a need to pass the execution context between those threads.
I've been trying to understand how ASP.NET 5 pipeline middlewares really work. A middleware, as I know, is just a Func<RequestDelegate, RequestDelegate>, which is a pointer to a method that receives a reference to the next request delegate and returns a new one that wraps the next. We can of course, use a class to represent a middleware, something like:
public class MyMiddleware
{
private readonly _next;
public MyMiddleware(RequestDelegate next)
{
if (next == null)
{
throw new ArgumentNullException("next");
}
_next = next;
}
public Task Invoke(HttpContext context)
{
// New request delegate code here which can wrap the next one on the pipeline
}
}
Since the RequestDelegate is a delegate that can hold references to methods which receives one HttpContext and returns a Task the Invoke method is the request delegate to be returned and which has access to the next one on the pipeline.
We have then, when coding a middleware, access to the next component of the pipeline, but there is a doubt I have. In the beginning I thought the ideal was always to work the following way:
Check if the middleware can handle the request
If it can, do whatever must be done with the HttpContext
Call the next middleware on the pipeline
So that when I studied this for the first time I thought each middleware should always call the next one. But doing this led to strange behavior as discussed on this question.
Also looking at the source code of some middlewares I see some of them follow another steps:
Check if the middleware can handle the request
If it can, do whatever must be done with the HttpContext and that's all
If not, and only if not call the next one
Is this the real idea of using middlewares? Which way is the correct approach at this? Each middleware do what must be done with the request and always invoke the next or if a midleware can handle the request it doesn't invoke the next anymore?
I believe a middleware should call the next only if it can't handle the request. The reason I think that is because if not, there would be coupling between the middlewares on the pipeline. So that to process the request the middleware would need to be aware of what the previous one did to avoid messing everything. Is this conclusion right?
Middleware exist to make the request pipeline modular, meaning that you can add/remove/replace parts from it as long as you respect the contract. For example, if your application serves some files without any caching, you can add a middleware at the front of the pipeline without altering the rest. They are building blocks.
A middleware can:
Do nothing and pass the request further (e.g. a middleware that is applicable only to POST requests but the current one is GET)
Do nothing to the request, do something else instead and pass it further (e.g. logging)
Do something to the request and pass the request further (e.g. get an authentication token and convert it to an identity, or remove some sensitive information from the request)
End the pipeline and not pass the request further (e.g. StaticFileMiddleware which just returns the file, or MVC when a route matches)
Probably answering your other question too: there are two types of middleware:
Middleware that are designed to do something and pass the data along further (etc. auth, cookies, validation, logging etc)
Middleware that complete the pipeline (static file, MVC, etc).
Of course, some might do both depending on the context. For example auth can end the pipeline if the credentials are incorrect but continue otherwise.
The author of the middleware must decide if the next middleware (if any) should be invoked. In the case of the middleware in your question which returns a message, it should not invoke the next one.