Как мне внедрить клиент Autorest с помощью TokenProvider с помощью autofac в asp.net mvc 5?

Я хочу правильно внедрить клиентскую зависимость autorest, которая использует api (у пользователей будет свой собственный токен для использования после входа в систему, но они могут использовать api перед входом в систему, для некоторых методов токен не требуется) с помощью autofac. Я знаю, что это не вопрос автореста, это больше касается autofac, но я хочу привести точный пример, чтобы получить лучшую рекомендацию (возможно, я все делаю неправильно, это концептуальная проблема). Я искал несколько примеров, которые я нашел, но во всех из них они были реализованы только для одного пользователя, они не использовали tokenprovider, они просто передали заранее известный токен (который не является токеном для пользователя, это для приложения).

Что я пробовал, так это зарегистрировать клиент autorest с завернутым параметром (уже зарегистрированные несколько зависимостей принимают друг друга в качестве параметров конструктора) в контейнер.

Вот как я регистрирую свои услуги:

protected void Application_Start()
{

    var builder = new ContainerBuilder();
    builder.RegisterControllers(Assembly.GetExecutingAssembly());


    var sp = ServicePointManager.FindServicePoint(new Uri(ConfigurationManager.AppSettings["WebApiBaseUrl"]));
    sp.ConnectionLeaseTimeout = 60 * 1000; // 1 minute
    builder.Register(c => new HttpContextWrapper(HttpContext.Current))
        .As<HttpContextBase>()
        .InstancePerRequest();
    builder.RegisterType<TokenProvider>().As<ITokenProvider>().InstancePerLifetimeScope();
    builder.RegisterType<TokenCredentials>().Keyed<ServiceClientCredentials>("credentials").InstancePerLifetimeScope();
    builder.RegisterType<WebApiClient>()
           .As<IWebApiClient>()
           .WithParameter("baseUri", new Uri(ConfigurationManager.AppSettings["WebApiBaseUrl"])
           ).WithParameter("credentials",
              new ResolvedParameter(
   (pi, ctx) => pi.ParameterType == typeof(ServiceClientCredentials),
   (pi, ctx) => ctx.ResolveKeyed<ServiceClientCredentials>(pi.Name))
   ).SingleInstance();


    IContainer container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

}

и мои услуги:

public partial class WebApiClient : ServiceClient<WebApiClient>, IWebApiClient
{

    public WebApiClient(System.Uri baseUri, ServiceClientCredentials credentials = null, params DelegatingHandler[] handlers) : this(handlers)
    {
        if (baseUri == null)
        {
            throw new System.ArgumentNullException("baseUri");
        }

        BaseUri = baseUri;

        if (credentials != null)
        {
            Credentials = credentials;
            Credentials.InitializeServiceClient(this);
        }
    }
}
public class TokenProvider : ITokenProvider
{
    private readonly HttpContextBase _context;

    public TokenProvider(HttpContextBase context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
    {
        // this should be async i know(another topic to ask in mvc 5)
        var token =_context.Session["ServiceToken"]?.ToString();
        if (string.IsNullOrWhiteSpace(token))
        {
            throw new InvalidOperationException("Could not get an access token from HttpContext.");
        }

        return new AuthenticationHeaderValue("Bearer", token);
    }
}

public class TokenCredentials : ServiceClientCredentials
{
    //I want to use this constructor
    public TokenCredentials(ITokenProvider tokenProvider);
}

Это исключение, которое я получаю

Внутреннее исключение Невозможно преобразовать объект типа Autofac.Core.ResolvedParameter в тип Microsoft.Rest.ServiceClientCredentials.


person Metehan Mutlu    schedule 31.05.2019    source источник
comment
Как вы регистрируете свои типы?   -  person Cyril Durand    schedule 31.05.2019
comment
Извините я забыл. отредактировал еще раз.   -  person Metehan Mutlu    schedule 31.05.2019


Ответы (2)


Не удалось преобразовать объект типа Autofac.Core.ResolvedParameter в тип Microsoft.Rest.ServiceClientCredentials.

означает, что вы используете объект ResolvedParameter, тогда как ожидается ServiceClientCredentials.

В вашем коде есть

.WithParameter("credentials",
     new ResolvedParameter(
       (pi, ctx) => pi.ParameterType == typeof(ServiceClientCredentials),
       (pi, ctx) => ctx.ResolveKeyed<ServiceClientCredentials>(pi.Name))
     )

WithParameter имеет 3 перегрузки:

  • WithParameter(string parameterName, object parameterValue) : когда вы знаете название параметра и можете указать его во время регистрации. Autofac создаст для вас объект NamedParameter.
  • WithParameter(Func<ParameterInfo, IComponentContext, bool> parameterSelector, Func<ParameterInfo, IComponentContext, object> valueProvider) : Когда вы не знаете имя параметра и/или когда вы не можете указать значение во время регистрации. Autofac создаст для вас объект ResolvedParameter.
  • WithParameter(Parameter parameter) : предоставить объект Parameter, который вы создаете сами.

В вашем случае вы используете первый вариант. Autofac создаст для вас NamedParameter, и вы укажете ResolvedParameter в качестве значения.

Чтобы исправить ошибку, вы не должны использовать первую перегрузку таким образом, но вы можете использовать вторую:

.WithParameter((pi, ctx) => pi.ParameterType == typeof(ServiceClientCredentials),
               (pi, ctx) => ctx.ResolveKeyed<ServiceClientCredentials>(pi.Name)))
person Cyril Durand    schedule 31.05.2019

Мой окончательный код выглядит примерно так. (Теперь он работает без проблем. Но если у вас есть какие-либо рекомендации или опасения по поводу этого кода, не стесняйтесь сообщить об этом.)

 builder.Register(c => new HttpContextWrapper(HttpContext.Current) as HttpContextBase)
               .As<HttpContextBase>().InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Request)
               .As<HttpRequestBase>().InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Response)
               .As<HttpResponseBase>().InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Server)
               .As<HttpServerUtilityBase>().InstancePerLifetimeScope();
            builder.Register(c => c.Resolve<HttpContextBase>().Session)
               .As<HttpSessionStateBase>().InstancePerLifetimeScope();
            builder.RegisterType<TokenProvider>().As<ITokenProvider>().InstancePerLifetimeScope();
            builder.RegisterType<TokenCredentials>().Keyed<ServiceClientCredentials>("credentials").InstancePerLifetimeScope();
            builder.RegisterType<WebApiClient>()
                   .As<IWebApiClient>()
                   .WithParameter("baseUri", new Uri(ConfigurationManager.AppSettings["WebApiBaseUrl"])
                   )
                   .WithParameter((pi, ctx) => pi.ParameterType == typeof(ServiceClientCredentials),
               (pi, ctx) => ctx.ResolveKeyed<ServiceClientCredentials>(pi.Name))
           .InstancePerLifetimeScope();

И мой поставщик токенов;

public async Task<AuthenticationHeaderValue> GetAuthenticationHeaderAsync(CancellationToken cancellationToken)
        {
            string token = "NonAuthorizedUserDummyToken";
            await Task.Delay(500);
            token = _context.Session?["ServiceToken"]?.ToString();
            return new AuthenticationHeaderValue("Bearer", token);
        }
person Metehan Mutlu    schedule 05.06.2019