2016-09-20 4 views
1

내 ASP.NET MVC 응용 프로그램과 주입 서비스 런타임 데이터를 전달하면 컨트롤러에 서비스를 주입하는 의존성 삽입 (Dependency Injection)를 사용합니다.은 의존성 주입

나는 내가 아는 한이 DI를 사용하여 생성자에 런타임 데이터를 전송하는 안티 패턴이 있기 때문에, 서비스에 런타임 데이터를 전달하는 몇 가지 방법을 찾을 필요가있다. 내 경우

나는 모든 서비스 사이에 다시 사용할 수있는 액세스 토큰에 의존 네 가지 서비스가 있습니다. 그러나 액세스 토큰이 만료 될 수 있으므로 만료 될 때 새로운 액세스 토큰을 발행해야합니다.

하는 서비스 (독립 NuGet 패키지) 만든 모든 요청에 ​​대한 액세스 토큰을 요구하는 다양한 서비스에 대한 모든 클라이언트입니다. 한 가지 예가 IUserServiceBusiness의 AddUserAsync 메소드입니다.이 메소드는 기본적으로 JSON 데이터로 끝점에 POST하고 권한 부여 헤더에 무기명 액세스 토큰을 추가합니다.

현재의 솔루션은 서비스의 모든 메소드에서 액세스 토큰을 매개 변수로 받아들이는 것입니다. 즉, 웹 애플리케이션이 액세스 토큰을 처리하고 필요할 때 전달하는 것을 의미합니다. 그러나이 솔루션은 냄새가 나기 때문에 더 나은 방법이 있어야합니다.

는 여기가 현재 어떻게하는지에 대한 예입니다.

구현의 모든 등록 된 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에 삽입됩니다. 사용자의 연령 업데이트 AccountController에서

private readonly IUserBusiness _userBusiness; 
    private readonly IAccessTokenBusiness _accessTokenBusiness; 

    public AccountController(IUserBusiness userBusiness, IAccessTokenBusiness accessTokenBusiness) 
    { 
     _userBusiness = userBusiness; 
     _accessTokenBusiness = accessTokenBusiness; 
    } 

예 엔드 포인트 : 여기

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

을 그리고 내가 생각 한 몇 가지 아이디어를 다음과 같습니다

  1. 서비스에 액세스 토큰 전달 setter를 사용하여 컨트롤러의 생성자에 저장합니다. 예를 들면 다음과 같습니다.

    모든 컨트롤러에서이 코드를 복제해야하기 때문에이 아이디어가 마음에 들지 않습니다.

  2. 서비스의 모든 방법으로 액세스 토큰을 (현재 이것을 수행 중)으로 전달하십시오. 예 :

    public ActionResult UpdateUser(int newAge) 
    { 
        string accessToken = _accessTokenBusiness.GetAccessToken(); 
        _userBusiness.UpdateAge(newAge, accessToken); 
    } 
    

    작동하지만 좋지 않습니다.

  3. IAccessTokenBusiness를 서비스 생성자에 전달합니다. 예를 들면 다음과 같습니다.

    IAccessTokenBusiness accessTokenBusiness = new AccessTokenBusiness(); 
    
    container.Register<IUserBusiness>(() => new IUserBusiness(accessTokenBusiness)); 
    

    그러나 액세스 토큰에 대해 캐싱을 처리하는 방법은 확실하지 않습니다. 아마도 AccessTokenBusiness의 생성자에 일반 ICache 구현을 허용 할 수 있으므로 하나의 캐싱 프레임 워크가 붙어 있지 않습니다.

나는 이것이 청결하고 영리한 방법으로 어떻게 풀릴 수 있는지 듣고 싶습니다.

감사합니다.

+0

당신은 실제로 그 액세스 토큰을 사용하여 몇 가지 논리를 보여줄 수 다음과 같이

container.Register<IUserBusiness, UserServiceBusiness>(); container.Register<IAccessTokenBusiness, AccessTokenBusiness>(); ICacheManager<object> cacheManager = CacheFactory.Build<object>(settings => settings.WithSystemRuntimeCacheHandle()); container.RegisterSingleton<ICacheManager<object>>(cacheManager); 

더 나아가, IAccessTokenBusiness의 장식을 추가 할 수 있습니다 다음과 같은 등록이 보인다? – Steven

+0

서비스 (독립적 인 NuGet 패키지)는 모든 요청에 ​​대해 액세스 토큰이 필요한 다양한 서비스의 모든 클라이언트입니다. 한 가지 예가 IUserServiceBusiness의 AddUserAsync입니다. 기본적으로 JSON 데이터로 끝점에 POST하고 베어러 액세스 토큰과 함께 Authroization 헤더를 추가합니다. – raRaRa

+0

일부 코드를 표시하십시오. – Steven

답변

0

외부 서비스와의 통신에이 액세스 토큰을 갖는 요구 사항은 실제로 해당 서비스를 호출 할 책임이있는 클래스의 구현 세부 사항입니다. 현재의 솔루션에서는 IUserBusiness 추상화가 해당 토큰을 노출하므로 이러한 구현 세부 정보가 누출됩니다. 이는 에 위반되는 내용으로 다음과 같이 나타납니다.

추상화는 세부 정보에 의존하지 않아야합니다.

경우 혹시 액세스 토큰을 필요로하지 않는 한이 IUserBusiness 구현을 변경, 당신이 기본적으로 당신이 Open/close Principle을 voilated 의미 당신의 코드베이스를 통해 전면적 인 변경을해야합니다 의미 할 것입니다.

해결책은 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.RegisterDecorator<IAccessTokenBusiness, CachingAccessTokenBusinessDecorator>(); 
+0

캐싱 데코레이터에 관한 한 가지 질문은 AccessTokenBusiness가 자체 NuGet 패키지에 정의되어 있다고 가정하면 캐싱 구현이 웹 사이트간에 변경 될 수 있기 때문에 웹 응용 프로그램에서 캐싱 데코레이터를 정의하고 구현할 수 있습니까? 다양한 구현을 수용하는 패키지 자체에 여러 캐싱 데코레이터를 제공합니까? 예 : 생성자에서 CacheManager를 허용 할 CacheManAccessTokenBusinessDecorator 또는 이것으로 트랙을 벗어나나요? 감사. – raRaRa

+0

흠. 그렇다면 캐싱 데코레이터가 생성자에서 허용하는 캐싱에 대한 추상화를 작성해야한다는 의미입니다. 그것은 나에게 좋을 것 같습니다. 왜냐하면 각 캐싱 프레임 워크마다 새로운 데코레이터를 만들 필요가 없기 때문에 인터페이스에 대한 새로운 래퍼를 작성하기 때문입니다. – raRaRa