我的ASP.NET MVC应用程序使用依赖注入将服务注入到控制器。
我需要找到一种将运行时数据传递给服务的方法,因为据我所知,使用DI将运行时数据发送给构造函数是一种反模式。
就我而言,我有四个不同的服务,它们都依赖于访问令牌,可以在这些服务之间重用它们。但是,该访问令牌可能会过期,因此在过期时需要采取一些措施来发行新的访问令牌。
服务(独立的NuGet包)是各种服务的所有客户端,每个请求都需要访问令牌。一个示例是IUserServiceBusiness中的AddUserAsync方法,它基本上使用JSON数据发布到端点,并使用承载访问令牌添加Authorization标头。
我当前的解决方案是接受访问令牌作为服务中所有方法的参数,这意味着Web应用程序会处理访问令牌并在需要时传递它们。但是这种解决方案有味道,必须有一种更好的方法来做到这一点。
这是有关当前操作方式的示例。
注册所有实现的RegisterContainer方法。
public static void RegisterContainers()
{
// Create a new Simple Injector container
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
SSOSettings ssoSettings = new SSOSettings(
new Uri(ConfigConstants.SSO.FrontendService),
ConfigConstants.SSO.CallbackUrl,
ConfigConstants.SSO.ClientId,
ConfigConstants.SSO.ClientSecret,
ConfigConstants.SSO.ScopesService);
UserSettings userSettings = new UserSettings(
new Uri(ConfigConstants.UserService.Url));
ICacheManager<object> cacheManager = CacheFactory.Build<object>(settings => settings.WithSystemRuntimeCacheHandle());
container.Register<IUserBusiness>(() => new UserServiceBusiness(userSettings));
container.Register<IAccessTokenBusiness>(() => new AccessTokenBusiness(ssoSettings, cacheManager));
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterMvcIntegratedFilterProvider();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
IUserBusiness和IAccessTokenBusiness的实现被注入到AccountController中。
private readonly IUserBusiness _userBusiness;
private readonly IAccessTokenBusiness _accessTokenBusiness;
public AccountController(IUserBusiness userBusiness, IAccessTokenBusiness accessTokenBusiness)
{
_userBusiness = userBusiness;
_accessTokenBusiness = accessTokenBusiness;
}
AccountController中用于更新用户年龄的示例端点:
public ActionResult UpdateUserAge(int age)
{
// Get accessToken from the Single Sign On service
string accessToken = _accessTokenBusiness.GetSSOAccessToken();
bool ageUpdated = _userBusiness.UpdateAge(age, accessToken);
return View(ageUpdated);
}
这是我想到的一些想法:
在控制器的构造函数中,使用设置器将访问令牌传递给服务。例如:
public HomeController(IUserBusiness userBusiness, IAccessTokenBusiness accessTokenBusiness)
{
_userBusiness = userBusiness;
_accessTokenBusiness = accessTokenBusiness;
string accessToken = _accessTokenBusiness.GetAccessToken();
_userBusiness.setAccessToken(accessToken);
}
我不喜欢这个主意,因为那样的话我将不得不在每个控制器中复制此代码。
将访问令牌与服务上的每种方法一起传递(当前正在执行此操作)。例如:
public ActionResult UpdateUser(int newAge)
{
string accessToken = _accessTokenBusiness.GetAccessToken();
_userBusiness.UpdateAge(newAge, accessToken);
}
可行,但我不喜欢它。
将IAccessTokenBusiness的实现传递给服务的构造函数。例如:
IAccessTokenBusiness accessTokenBusiness = new AccessTokenBusiness();
container.Register<IUserBusiness>(() => new IUserBusiness(accessTokenBusiness));
但是我不确定如何处理访问令牌的缓存。也许我可以让AccessTokenBusiness的构造函数接受一些通用的ICache实现,这样我就不必再局限于一个缓存框架了。
我很想听听如何以一种干净,聪明的方式解决这个问题。
谢谢!
正如我所看到的,拥有与外部服务通信的访问令牌的要求是该类的实现细节,该类实际上负责调用该服务。在当前解决方案中,您正在泄漏这些实现细节,因为IUserBusiness
抽象公开了该令牌。这违反了依赖反转原则,该原则规定:
抽象不应依赖细节。
万一您将此IUserBusiness
实现更改为不需要访问令牌的实现,则意味着您将不得不在代码库中进行全面更改,这基本上意味着您破坏了Open / close Principle。
解决方案是让IUserBusiness
实现依赖于IAccessTokenBusiness
自身。这意味着您的代码将如下所示:
// HomeController:
public HomeController(IUserBusiness userBusiness)
{
_userBusiness = userBusiness;
}
public ActionResult UpdateUser(int newAge)
{
bool ageUpdated = _userBusiness.UpdateAge(newAge);
return View(ageUpdated);
}
// UserBusiness
public UserBusiness(IAccessTokenBusiness accessTokenBusiness)
{
_accessTokenBusiness = accessTokenBusiness;
}
public bool UpdateAge(int age)
{
// Get accessToken from the Single Sign On service
string accessToken = _accessTokenBusiness.GetSSOAccessToken();
// Call external service using the access token
}
但是我不确定如何处理访问令牌的缓存。
这既不是控制器的问题,也不是业务逻辑的问题。这是AccessTokenBusiness
实现的关注点,还是周围的装饰器IAccessTokenBusiness
。具有装饰器是最明显的解决方案,因为它使您可以独立于访问令牌的生成来更改缓存。
请注意,您可以通过使用容器的自动装配功能来稍微简化配置。您可以让容器分析类型的构造函数并自行确定要注入的内容,而不是使用委托注册类。此类注册如下所示:
container.Register<IUserBusiness, UserServiceBusiness>();
container.Register<IAccessTokenBusiness, AccessTokenBusiness>();
ICacheManager<object> cacheManager =
CacheFactory.Build<object>(settings => settings.WithSystemRuntimeCacheHandle());
container.RegisterSingleton<ICacheManager<object>>(cacheManager);
此外,IAccessTokenBusiness
可以添加的装饰器,如下所示:
container.RegisterDecorator<IAccessTokenBusiness, CachingAccessTokenBusinessDecorator>();
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句