目录
这是4部分系列文章的第1部分,讨论如何使用面向方面编程的身份验证、授权、验证和日志记录来改进WCF服务。本文涵盖了基础知识。
由于这将是一篇很长的文章,因此我将其分为几个部分以使其更具可读性。
这是第一部分“介绍和基础”。可以通过单击以下链接找到下一部分:
介绍
本文基于我在职业生涯中完成的一个重构项目。由于安全策略的变化,我被要求重构现有的WCF服务,以使其更加安全和可追溯。我决定将授权、身份验证、日志记录和验证概念引入现有的服务架构,而不是重写整个服务代码。
第一个想法是重写所有服务,但由于我们目前使用了数百种服务方法,这种想法会带来大量的开发时间。相反,我决定使用面向方面的编程来减少开发工作。
背景
有几个很好的AOP库可以与C#一起使用,例如Castle、AspectSharp、Aspect.NET和PostSharp,由于易用性和良好的文档以及社区上的示例数量,为此项目选择了PostSharp。
Microsoft SQL Server用于存储数据,Entity Framework用于数据访问。尽管.NET Core可能是一种现代方法,但由于我们试图减少开发工作,因此使用.NET Framework是为了向后兼容。
使用代码
作为一种常见的做法,大多数开发人员编写WCF服务如下:
[ServiceContract]
public interface IExampleService
{
[OperationContract]
int CreatePlayer(string name, DateTime dateofBirth, int? height, int? weight, string club);
[OperationContract]
Player GetPlayerById(int id);
[OperationContract]
List<Player> GetAllPlayers();
[OperationContract]
List<Player> GetClubPlayers(string club);
}
public class ExampleService : IExampleService
{
public int CreatePlayer(string name, DateTime dateofBirth, int? height,
int? weight, string club)
{
return PlayerRepository.CreateNewPlayer(name, dateofBirth, height, weight, club);
}
public List<Player> GetAllPlayers()
{
return PlayerRepository.GetAllPlayers();
}
public List<Player> GetClubPlayers(string club)
{
return PlayerRepository.GetClubPlayers(club);
}
public Player GetPlayerById(int id)
{
return PlayerRepository.GetPlayerById(id);
}
}
我们的第一个动作是将多个输入参数更改为包含参数的类作为数据协定和命名约定,因为它们是请求的一部分,所以将它们命名为方法名称 + 请求。
[DataContract]
public class CreatePlayerRequest
{
[DataMember]
public string Name { get; set; }
[DataMember]
public DateTime DateOfBirth { get; set; }
[DataMember]
public int? Height { get; set; }
[DataMember]
public int? Weight { get; set; }
[DataMember]
public string Club { get; set; }
}
[DataContract]
public class GetClubPlayersRequest
{
[DataMember]
public string Club { get; set;}
}
[DataContract]
public class GetPlayerByIdRequest
{
[DataMember]
public int PlayerId { get; set; }
}
然后为每个特定的服务方法创建响应对象,再次使用命名约定方法名称 + 响应。
[DataContract]
public CreatePlayerResponse
{
[DataMember]
public int PlayerId { get; set; }
}
[DataContract]
public GetAllPlayerResponse
{
[DataMember]
public List<Player> PlayerList { get; set; }
}
[DataContract]
public GetClubPlayersResponse
{
[DataMember]
public List<Player> PlayerList { get; set; }
}
[DataContract]
public GetPlayerByIdResponse
{
[DataMember]
public Player Player { get; set; }
}
当然,也可以使用一个Response对象代替GetAllPlayerResponse和GetClubPlayersResponse对象。
下一步是创建一个名为“WCFServices.Shared”的新类库项目并将其引用到我们现有的服务。创建并引用这个项目后,让我们分别创建这两个类。
[DataContract]
public class BaseRequest
{
[DataMember]
public string Username { get; set; }
[DataMember]
public string Password { get; set; }
}
[DataContract]
public class BaseResponse
{
[DataMember]
public bool IsException { get; set; }
[DataMember]
public bool IsSuccess { get; set; }
[DataMember]
public string[] Messages { get; set; }
}
新创建的BaseRequest类将是我们所有请求对象的基类,并将保存身份验证和授权的必要信息。在类似的方法中,BaseResponse类将是我们所有响应对象的基类,并将在三个属性中保存所有方法的响应状态信息:
- IsException:如果在方法调用期间发生异常,将设置为true。如果将发生异常,则应将IsSuccess属性设置为false,并且该Messages属性应返回异常的Message属性。
- IsSuccess:如果方法以成功的方式完成,将设置为true。如果将其设置为true,则应将IsException属性设置为false并将Messages属性设置为常量消息。
- Messages:此属性可能会返回一行或多行消息,通知客户端有关请求的状态。除了有关异常(如果发生)或成功消息的详细信息,此属性还可以保存失败验证的值。
创建这些类后,服务中的所有请求和响应类都应更新如下:
[DataContract]
public class CreatePlayerRequest : BaseRequest
{
[DataMember]
public string Name { get; set; }
[DataMember]
public DateTime DateOfBirth { get; set; }
[DataMember]
public int? Height { get; set; }
[DataMember]
public int? Weight { get; set; }
[DataMember]
public string Club { get; set; }
}
[DataContract]
public class GetClubPlayersRequest : BaseRequest
{
[DataMember]
public string Club { get; set;}
}
[DataContract]
public class GetPlayerByIdRequest : BaseRequest
{
[DataMember]
public int PlayerId { get; set; }
}
[DataContract]
public CreatePlayerResponse : BaseResponse
{
[DataMember]
public int PlayerId { get; set; }
}
[DataContract]
public GetAllPlayersResponse : BaseResponse
{
[DataMember]
public List<Player> PlayerList { get; set; }
}
[DataContract]
public GetClubPlayersResponse : BaseResponse
{
[DataMember]
public List<Player> PlayerList { get; set; }
}
[DataContract]
public GetPlayerByIdResponse : BaseResponse
{
[DataMember]
public Player Player { get; set; }
}
我们的服务和接口将是这样的:
[ServiceContract]
public interface IExampleService
{
[OperationContract]
CreatePlayerResponse CreatePlayer(CreatePlayerRequest request);
[OperationContract]
GetPlayerByIdResponse GetPlayerById(GetPlayerByIdRequest request);
[OperationContract]
GetAllPlayersResponse GetAllPlayers(BaseRequest request);
[OperationContract]
GetClubPlayersResponse GetClubPlayers(GetClubPlayersRequest request);
}
public class ExampleService : IExampleService
{
public CreatePlayerResponse CreatePlayer(CreatePlayerRequest request)
{
try
{
return new CreatePlayerResponse
{
PlayerId = PlayerRepository.CreateNewPlayer(request.Name,
request.DateOfBirth, request.Height, request.Weight, request.Club),
IsException = false,
IsSuccess = false,
Messages = new string[] { "Operation successful" }
};
}
catch(Exception ex)
{
return new CreatePlayerResponse
{
IsException = true,
IsSuccess = false,
Messages = new string[] { ex.Message; }
};
}
}
public GetAllPlayersResponse GetAllPlayers(BaseRequest request)
{
try
{
return new GetAllPlayersResponse
{
PlayerList = PlayerRepository.GetAllPlayers(),
IsException = false,
IsSuccess = false,
Messages = new string[] { "Operation successful" }
};
}
catch(Exception ex)
{
return new GetAllPlayersResponse
{
IsException = true,
IsSuccess = false,
Messages = new string[] { ex.Message; }
};
}
}
// I think the first two methods give enough clue about the transformation.
:
:
}
这是我们系列的第一部分,解释了对我们的服务结构进行一些更新并为我们的下一章做准备的基本方法。
您可以在此处阅读下一部分(验证)。
https://www.codeproject.com/Articles/5293890/Improving-WCF-Service-Quality-Part-1-Introduction