question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

CancellationToken parameters in web apis appear as an expanded struct

See original GitHub issue

for example

[HttpGet("SwaggerTest")]
public async Task SwaggerTest(CancellationToken cancellationToken)

is generated as

System.Threading.Tasks.Task SwaggerTestAsync(bool? isCancellationRequested, bool? canBeCanceled, IntPtr waitHandle_Handle, bool? waitHandle_SafeWaitHandle_IsInvalid, bool? waitHandle_SafeWaitHandle_IsClosed);

whereas the CancellationToken should be ignored when generating the client (CancellationTokens in MVC controllers are automagically bound to a per request token that is cancelled if the client connection drops).

swagger seems to be treating the token as a complex query parameter and expanding it as per https://github.com/RicoSuter/NSwag/issues/96

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
blushingpenguincommented, Aug 13, 2019

It appears that this is because I had the compatibility version for Mvc set to 2.0. With compatibility version set to >= 2.1 then cancellation token parameters are filtered out by the api (and thus both my problems are solved).

https://github.com/aspnet/AspNetCore/issues/13045

Sorry for the noise!

0reactions
blushingpenguincommented, Aug 11, 2019

I think I’ve mixed up api explorer (being the ui) and Microsoft.AspNetCore.Mvc.ApplicationModels in what I said above. I’ve done quite a bit more digging, and Microsoft.AspNetCore.Mvc.ApplicationModels is the thing that is reporting CancellationToken as an action parameter. I’m not sure if that behaviour correct or not, but the nswag WebApi generator does contains an OperationProcessor that filters out cancellation tokens explicitly:

NSwag\src\NSwag.Generation.WebApi\Processors\OperationParameterProcessor.cs:

        public bool Process(OperationProcessorContext context)
        {
            var httpPath = context.OperationDescription.Path;
            var parameters = context.MethodInfo.GetParameters();

            var position = 1;
            foreach (var contextualParameter in parameters.Select(p => p.ToContextualParameter())
                .Where(p => p.Type != typeof(CancellationToken) &&
                            !p.ContextAttributes.GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name).Any() &&
                            !p.ContextAttributes.GetAssignableToTypeName("FromServicesAttribute", TypeNameStyle.Name).Any() &&
                            !p.ContextAttributes.GetAssignableToTypeName("BindNeverAttribute", TypeNameStyle.Name).Any()))

the AspNetCore generator doesn’t appear to have an equivalent of this which explains why it expands them as structs. I can work around this by implementing an IActionDescriptorProvider that strips off CancellationToken parameters so nswag never sees them:

public class RemoveCancellationTokenActionDescriptorProvider : IActionDescriptorProvider
{
    public int Order => 1;

    public void OnProvidersExecuting(ActionDescriptorProviderContext context)
    {
        foreach (var descriptor in context.Results)
        {
            for (int i = 0; i < descriptor.Parameters.Count; ++i)
            {
                if (descriptor.Parameters[i].ParameterType == typeof(CancellationToken))
                {
                    descriptor.Parameters.RemoveAt(i);
                    --i;
                }
            }
        }
    }

    public void OnProvidersExecuted(ActionDescriptorProviderContext context)
    {
    }
}
...
services.AddTransient<IActionDescriptorProvider, RemoveCancellationTokenActionDescriptorProvider>();

However I think it’s relatively easy to filter out cancellation tokens in the same way on the aspnet core side (if you think that’s correct). I had a bit of trouble reproducing this in the tests in the nswag codebase though (I was trying to add a test that would fail but couldn’t figure out where exactly to put it to tickle this problem) – if you would be so kind as to point me in the right direction I will happily have a go at implementing that.

I also encountered the issue with the exception above, which is simply a bug in Microsoft.AspNetCore.Mvc.ApplicationModels.InferParameterBindingInfoConvention.InferBindingSourceForParameter that does not handle CancellationToken. In other places in the same api the binding source for CancellationToken is inferred correctly (as BindingSource.Special)

(I worked around that by copy/pasting InferParameterBindingInfoConvention and ApiBehaviorApplicationModelProvider and adding the relevant check to InferBindingSourceForParameter to return BindingSource.Special for the CancellationToken parameter, which is how it is handled elsewhere).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why CancellationToken is a struct?
I would note that the current value type implementation is exactly the same size as a reference so there isn't any additional copying...
Read more >
CA1068: CancellationToken parameters must come last
A method has a CancellationToken parameter that is not the last parameter. By default, this rule analyzes the entire codebase, ...
Read more >
Best practices for REST API design - Stack Overflow Blog
In this article, we'll look at how to design REST APIs to be easy to understand for anyone consuming them, future-proof, and secure...
Read more >
Accepting Raw Request Body Content with ASP.NET Web API
One ASP.NET Web API related question that frequently comes up frequently is how to capture the raw request content to a simple parameter...
Read more >
REST API Testing Strategy: What Exactly Should You Test?
Here's a technical look at how we test our APIs. ... Extended positive testing with optional parameters ... Content with wrong structure. Overflow...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found