我应该如何在 asp.net mvc 5 中使用 autofac 将 TokenProvider 注入 Autorest 客户端?

How should i inject Autorest Client with TokenProvider using autofac in asp.net mvc 5?

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

这是我得到的异常

Inner exception Unable to cast object of type Autofac.Core.ResolvedParameter to type Microsoft.Rest.ServiceClientCredentials.

Unable to cast object of type Autofac.Core.ResolvedParameter to type 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) :当您不知道参数的名称时 and/or 当您无法在注册时提供值时。 Autofac 将为您创建一个 ResolvedParameter 对象。
  • WithParameter(Parameter parameter) : 提供您自己创建的 Parameter 对象。

在您的情况下,您使用的是第一个选项。 Autofac 将为您创建一个 NamedParameter,您提供一个 ResolvedParameter 作为值。

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

.WithParameter((pi, ctx) => pi.ParameterType == typeof(ServiceClientCredentials),
               (pi, ctx) => ctx.ResolveKeyed<ServiceClientCredentials>(pi.Name)))

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

 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);
        }