0

我想通过使用 autofac 正确注入一个使用 api 的 autorest 客户端依赖项(用户在登录后将有自己的令牌使用,但他们可以在登录前使用 api,因为某些方法不需要令牌)。我知道这不是直接的 autorest 问题,它更多的是关于 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

4

2 回答 2

1

无法将类型的对象Autofac.Core.ResolvedParameter转换为类型Microsoft.Rest.ServiceClientCredentials

表示您正在使用一个ResolvedParameter对象,而 aServiceClientCredentials是预期的。

在您的代码中有

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

WithParameter3 个重载:

  • 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 并且您提供 aResolvedParameter作为值。

要修复错误,您不应以这种方式使用第一个重载,但可以使用第二个:

.WithParameter((pi, ctx) => pi.ParameterType == typeof(ServiceClientCredentials),
               (pi, ctx) => ctx.ResolveKeyed<ServiceClientCredentials>(pi.Name)))
于 2019-05-31T09:36:05.953 回答
0

我的最终代码现在是这样的。(它现在可以正常工作。但是如果您对此代码有任何建议或疑虑,请随时告诉。)

 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);
        }
于 2019-06-05T13:05:25.400 回答