我应该如何在 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);
}
我想正确地注入一个使用 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 typeMicrosoft.Rest.ServiceClientCredentials
.
Unable to cast object of type
Autofac.Core.ResolvedParameter
to typeMicrosoft.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);
}