What is the concept behind the Ifilter.Allowmultiple property?

Asked

Viewed 34 times

0

When studying the functionality of the "Web API Pipeline . NET Faramework", I came across the property "Allowmultiple", brought by inheritance of the abstract class "Ifilter".

Microsoft documentation informs that it is a boolean property that "obtains or defines a value indicating whether more than one instance of the specified attribute can be specified for a single program element".

However, either I did not understand the concept, or I did not understand the use. Even if I set the value of this property to true (true), I cannot use more than one instance of the filter in the same program element.

Below is the implementation of my filter code:

    public class BasicAuthenticationFilter : Attribute, IAuthenticationFilter
{    
    public bool AllowMultiple { get { return true; } }

    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        var authorization = context.Request.Headers.Authorization;
        if (authorization != null &&
            string.Equals(authorization.Scheme, "Basic", StringComparison.OrdinalIgnoreCase) &&
            !string.IsNullOrEmpty(authorization.Parameter))
        {
            if (ExtractUserNameAndPassword(authorization.Parameter, out string userName, out string password) &&
                userName == "userName" && password == "123qwe")
            {
                var identity = new GenericIdentity(userName, "Basic");
                context.Principal = new GenericPrincipal(identity, null);
            }
            else
            {
                context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", context.Request);
            }
        }
        else
        {
            context.ErrorResult = new AuthenticationFailureResult("Missing auth", context.Request);
        }
        return Task.FromResult(0);
    }

    public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
    {
        var host = context.Request.RequestUri.DnsSafeHost;
        context.Result = new AddChallengeOnUnauthorizedResult(new AuthenticationHeaderValue("Basic", "realm=\"" + host + "\""), context.Result);
        return Task.CompletedTask;
    }

    public class AddChallengeOnUnauthorizedResult : IHttpActionResult
    {
        private readonly AuthenticationHeaderValue _challenge;
        private readonly IHttpActionResult _innerResult;

        public AddChallengeOnUnauthorizedResult(AuthenticationHeaderValue challenge, IHttpActionResult innerResult)
        {
            _challenge = challenge;
            _innerResult = innerResult;
        }

        public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = await _innerResult.ExecuteAsync(cancellationToken);
            if (response.StatusCode == HttpStatusCode.Unauthorized && response.Headers.WwwAuthenticate.All(h => h.Scheme != _challenge.Scheme))
                response.Headers.WwwAuthenticate.Add(_challenge);
            return response;
        }
    }

    private bool ExtractUserNameAndPassword(string authorizationParameter, out string userName, out string password)
    {
        userName = null;
        password = null;
        byte[] credentialBytes;
        try
        {
            credentialBytes = Convert.FromBase64String(authorizationParameter);
        }
        catch (FormatException)
        {
            return false;
        }

        var encoding = System.Text.Encoding.GetEncoding("ISO-8859-1");
        var decodedCredentials = encoding.GetString(credentialBytes);
        if (string.IsNullOrEmpty(decodedCredentials))
        {
            return false;
        }

        int colonIndex = decodedCredentials.IndexOf(':');
        if (colonIndex == -1)
        {
            return false;
        }

        userName = decodedCredentials.Substring(0, colonIndex);
        password = decodedCredentials.Substring(colonIndex + 1);
        return true;
    }

    public class AuthenticationFailureResult : IHttpActionResult
    {
        public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
        {
            ReasonPhrase = reasonPhrase;
            Request = request;
        }

        public string ReasonPhrase { get; private set; }

        public HttpRequestMessage Request { get; private set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(Execute());
        }

        private HttpResponseMessage Execute()
        {
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
            response.RequestMessage = Request;
            response.ReasonPhrase = ReasonPhrase;
            return response;
        }
    }
}

Using this class as attribute more than once in a single program element generates build error (Duplicate "Basicauthenticationfilter" attribute) in the second citation of the attribute :

[BasicAuthenticationFilter]
[BasicAuthenticationFilter]

public class StudioGhibliController : ApiController
{
    public HttpClient HttpClient { get; set; }
    public string ApiUrlPrefix { get; set; }
    public HttpResponseMessage Response { get; set; }
    public StudioGhibliController()
    {...

What would then be the concept and correct use of this property? What is it really for?

1 answer

1


let’s simplify things a little. I think through an example things get easier. I created an example project where you can test the concept. To advance we go there: I created an Asp.net web api project and in it I created a class where I implemented the Iactionfilter interface.

 public class MyFilter : IActionFilter
{
    //mude para true e observe o Output
    public bool AllowMultiple => false;

    private string name;

    public MyFilter(string nome)
    {
        name = nome;
    }

    public MyFilter()
    {

    }

    public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        Debug.WriteLine($"Passando pelo MyFilter {name}");
        var r = await  continuation.Invoke();
        return r;


    }
}

In the Webapiconfig.Cs class add the following lines:

 public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();
        config.Filters.Add(new MyFilter("Filtro A"));
        config.Filters.Add(new MyFilter("Filtro B"));

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Now testing with the Allowmultiple set in true: The result was: inserir a descrição da imagem aqui

When setting Allomultiple to false:

inserir a descrição da imagem aqui

Well, we can conclude that every time I need to perform some action on these filters where I must have two separate events for whatever reason I can go there and add more than one instance to my filter. Otherwise I Seto Allowmultiple as false and only the last one added in the Webapiconfig class will be used because the order you add matters.

I hope I helped you.

  • Perfect. Concept and assimilated use. Thank you very much, master!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.