Servant를 사용하여 문제가 발생했으며 사용 사례가 원래의 질문자와 비슷하다고 생각됩니다.
type TokenProtect db = AuthProtect "auth-token"
type instance AuthServerData (TokenProtect db) = DatabaseAuthResult db
으로 : 기본적으로 나는 따라서 원래 포스터의 코드 같은 것을 필요로
class IsDatabase db where
type DatabaseAuthResult db :: *
instance IsDatabase MyDBType
type DatabaseAuthResult MyDBType = DBUser
처럼, 내 처리기를 통해 클래스가 제공하는 특정 유형의 동의어 제약 유형을 스레드 할 수 있도록 AuthProtect를 원 내가 해결할 수있는 한, Servant의 general auth implementation 구조 내에서는 불가능합니다. 선인장의 대답은 정확하게 newtype에서 실존 적을 포장해야한다고 말합니다. 그러나 그 자체로는 서번트 제약 조건과 관련된 컴파일 오류로 이어지기 때문에 HasServer
인스턴스와 관련된 문제 일 수 있습니다.
그러나 서번트의 AuthProtect
, AuthHandler
등을 사용자 고유의 구현으로 복제하고이를 위해 자체 서버 버전을 작성하는 것이 일반적인 문제입니다.
-- import for all the internal servant stuff like addAuthCheck
import Servant.Server.Internal.RoutingApplication
data DBAuthProtect (tag :: k) db deriving (Typeable)
newtype DBAuthHandler r db result = DBAuthHandler {unDBAuthHandler :: r -> Handler result}
instance (HasServer api context
, HasContextEntry context (DBAuthHandler Request db (AuthServerData (DBAuthProtect tag db))))
=> HasServer (DBAuthProtect tag db :> api) context where
type ServerT (DBAuthProtect tag db :> api) m = AuthServerData (DBAuthProtect tag db) -> ServerT api m
route Proxy context subserver =
route (Proxy :: Proxy api) context (subserver `addAuthCheck` withRequest authCheck)
where
authHandler :: Request -> Handler (AuthServerData (DBAuthProtect tag db))
authHandler = unDBAuthHandler (getContextEntry context)
authCheck :: Request -> DelayedIO (AuthServerData (DBAuthProtect tag db))
authCheck = (>>= either delayedFailFatal return) . liftIO . runExceptT . authHandler
그런 다음 AuthProtect
유사 이것을 사용할 수 있습니다, 그래서 뭔가 마지막
type TokenProtect db = DBAuthProtect "auth-token" db
type instance AuthServerData (TokenProtect db) = DatabaseAuthResult db
type ProtectedAPI db = "private" :> TokenProtect db :> Get [...]
dbAuthHandler :: (IsDatabase db) => db -> DBAuthHandler Request db (DatabaseAuthResult db)
dbAuthHandler db = DBAuthHandler $ \ req -> do
-- req :: Request
-- ... do some work here and return a type (DatabaseAuthResult db), so for MyDBType you would return DBUser - you have both the db itself and the request to work with
처럼이 종의
serveWithContext
를 사용하여 모두 함께이 문제를 놓고 상황에서 당신이 핸들러가 부분적으로
mkContext :: db -> Context '[DBAuthHandler Request db (AuthServerData db)]
mkContext db = dbAuthHandler db :. EmptyContext
main :: IO()
main = do
db <- getMyDBSomehow -- a concrete type, say MyDBType
let myApi = (Proxy :: Proxy (ProtectedAPI MyDBType))
serveWithContext myApi (mkContext db) handlers
을 적용 제공
기본적으로이 방법은 다양한 변수와 비트를 통해 유형 변수를 스레드하여 api 매개 변수로 끝내는 것입니다 (핸들러와 마찬가지로) db 형식으로 처리되므로 API 유형 및 처리기에서 유형 동의어를 사용할 수 있습니다.
당신이 당신의 authHandler을 실행할 때 enter
를 사용하여이 패턴 개선 (및 앱 모나드는 당신 serveWithContext
에 전달 컨텍스트에 필요가있는 컨텍스트를 추가 할 수 있습니다 응용 프로그램에 대한 사용자 정의 모나드를 사용하는 경우, 그러나 그것은 넘어 이 질문의 범위 ...).
'TokenProtect'은 타입 동의어이며 오른쪽에'auth'를 사용하지 않습니다. 두 번째 줄에서 타입 동의어를 확장하면'AuthServerData (AuthProtect "auth-token") = Id auth' 형식이되므로 오른쪽의'auth'는 효과적으로 언 바인드됩니다. – kosmikus
이 유형의 가족이 이루고자하는 목표와 관련하여 좀 더 폭 넓은 컨텍스트가 없어도이를 '수정'할 수있는 방법이 없습니다. 예 : 'AuthServerData'에 매개 변수를 추가하십시오 : type instance AuthServerData (AuthProtect "auth-token") auth = Id auth' – user2407038
수정이'type' 대신에'newtype'을 사용하고 있다고 확신합니다. – Carl