これが私の契約です:
[ServiceContract]
public interface IMyServiceContract {
[OperationContract]
OperationResponse1 Operation1(OperationRequest1 req);
[OperationContract]
OperationResponse2 Operation2(OperationRequest2 req);
}
OperationRequest1とOperationRequest2はどちらもBaseOperationRequestから継承します。これは、サービスに着信するすべての要求の資格情報を保持します。
[MessageContract]
public abstract class BaseOperationRequest {
[MessageHeader(MustUnderstand = true)]
public Guid Token { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Password { get; set; }
private User User { get; set; }
}
OperationResponse1とOperationResponse2はどちらも基本クラスから継承します。
[MessageContract]
public abstract class BaseOperationResponse {
[MessageHeader(MustUnderstand = true)]
public bool Success { get; set; }
[MessageHeader(MustUnderstand = true)]
public ServiceErrors ErrorCode { get; set; }
}
ErrorCodeは列挙型です。
リクエストでわかるように、2つのメッセージヘッダーと、SOAPメッセージの一部として逆シリアル化されない1つの内部オブジェクトがあります。これは、サービス実装で処理される前に、このオブジェクトをリクエストに挿入したいためです。すべての単一の操作の実装はこのオブジェクトを使用します。各操作でデータレイヤーを2回呼び出す必要はありません。
(属性を介して)WCF拡張性を使用して、次の2つのタスクを実行したいと思います。
- 要求しているユーザーを認証します。
- すべての操作で使用するために、着信要求クラスの「User」に複合/複合ビジネス・オブジェクトを移入します。
IOperationInvoker、IDispatchMessageFormatter、およびIDispatchMessageInspectorを調査しましたが、これらのいずれも十分に適しているとは言えません。
参考までに、これが私のサービスの基本的な実装例であり、WCFの拡張性(またはリポジトリ/データレイヤーの呼び出し)はありません。
public class MyService: IMyServiceContract {
public OperationResponse1 Operation1(OperationRequest1 req) {
if(req.Token == new Guid("GUID VALUE") && req.Password == "password") {
// Perform some actions....
return new OperationResponse1 {
Success = true
}
} else {
return new OperationResponse1 {
Success = false,
Error = "You are not authenticated"
}
}
}
public OperationResponse2 Operation2(OperationRequest2 req) {
if(req.Token == new Guid("GUID VALUE") && req.Password == "password") {
// Perform some actions....
return new OperationResponse2 {
Success = true
}
} else {
return new OperationResponse2 {
Success = false,
Error = "You are not authenticated"
}
}
}
}
IOperationInvokerが最も適切な拡張ポイントのようですが、操作を「キャンセル」してクライアントへの応答をオーバーライドする方法を完全に理解することはできません。これが私が到達した場所です:
/// <summary>
/// Provides an invoker that can be used to authenticate a BaseOperationRequest message.
/// </summary>
public class UserAuthenticationInvoker : IOperationInvoker {
/// <summary>
/// The original operation invoker.
/// </summary>
private IOperationInvoker _originalInvoker;
/// <summary>
/// The injected User service, for authentication.
/// </summary>
[Inject]
public IUserService UserService { get; set; }
public UserAuthenticationInvoker(IOperationInvoker originalInvoker) {
_originalInvoker = originalInvoker;
}
#region Implementation of IOperationInvoker {
public object[] AllocateInputs() {
return _originalInvoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs) {
// Validate base request
if(!(inputs[0] is BaseOperationRequest)) {
throw new ArgumentException("The request object must inherit from BaseOperationRequest in order for User authentication to take place.");
}
// Get BaseOperationRequest
var req = (BaseOperationRequest)inputs[0];
// Authenticate the User
var authResult = UserService.AuthenticateUser(new AuthenticateUserRequest {
Token = req.Token,
Password = req.Password
});
if(authResult.Success) {
// This is where I get stuck - do I need to modify "outputs"? If so, how do I tell the invoker that I want a particular response to be returned, and to cancel the rest of the operation?
return _originalInvoker.Invoke(instance, inputs, outputs);
}
return _originalInvoker.Invoke(instance, inputs, out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) {
throw new NotImplementedException("The operation cannot be invoked asynchronously.");
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) {
throw new NotImplementedException("The operation cannot be invoked asynchronously.");
}
public bool IsSynchronous {
get { return true; }
}
#endregion
}