目录
介绍
使用代码
介绍我们在前面的部分中讨论了这个重构过程的基础知识。这部分解释了我如何将验证添加到WCF服务。
使用代码要添加验证,让我们添加一个名为WCFService.Utils的新类库项目到解决方案,然后添加一个名为Validation的文件夹到项目。
然后,在继续之前,让我们回到WCFService.Shared项目并在Objects文件夹下添加一个名为ValidationResult的类,如下所示:
public class ValidationResult
{
public bool IsValidated { get; set; }
public List Messages { get; set; }
}
其中IsValidated属性将保存验证结果,Messages属性将保存验证错误消息(如果有)。
现在返回到WCFService.Utils项目,添加一个名为IBaseValidator的接口和一个名为BaseValidator的类,该类继承了Validation文件夹下的IBaseValidator。
public interface IBaseValidator
{
ValidationResult Validate(BaseRequest request);
}
public class BaseValidator : IBaseValidator
{
public virtual ValidationResult Validate(BaseRequest request)
{
bool validationResult = false;
List messages = new List();
if (string.IsNullOrWhiteSpace(request.Username))
{
validationResult = false;
messages.Add("Empty username.");
}
if (string.IsNullOrWhiteSpace(request.Password))
{
validationResult = false;
messages.Add("Empty password");
}
return new ValidationResult
{
IsValidated = validationResult,
Messages = messages
};
}
}
关于BaseValidator类,我想注意几点。首先,由于我们将使用身份验证,并且将从BaseRequest类中继承每个请求对象,因此每个传入的请求都将保留Username和Password值。由于这些字段是身份验证所必需的,因此应验证每个请求是否存在其属性。这就是我们将这些验证放在基类中的原因。还要标记该Validate函数被标记为virtual。这有两个目的。首先,允许从该基类继承的任何验证类实现它们自己的验证过程(甚至对于Username和Password属性)并统一所有派生验证类的实现。
然后,我们通过在每个服务库下的Validators文件夹下创建验证器,开始为所需的每个请求实现验证器。让我们创建并检查我们的第一个验证器,它将验证玩家创建过程的请求。
internal class CreatePlayerValidator : BaseValidator
{
public override ValidationResult Validate(BaseRequest req)
{
ValidationResult result = base.Validate(req);
if (!result.IsValidated)
return result;
else
{
bool validationResult = true;
List messages = new List();
CreatePlayerRequest request = req as CreatePlayerRequest;
if (request == null)
return new ValidationResult
{
IsValidated = false,
Messages = new List { "Can not convert request object
or request object null" }
};
if (string.IsNullOrWhiteSpace(request.Name))
{
validationResult = false;
messages.Add("Name attribute can not be null when creating a player");
}
if (request.DateOfBirth.AddYears(10) > DateTime.Now)
{
validationResult = false;
messages.Add("A player must be at least 10 years old to be registered");
}
if (request.Height.HasValue && request.Height.Value < 100)
{
validationResult = false;
messages.Add("A player must be at least 10 cm tall to be registered");
}
if (request.Height.HasValue && request.Height.Value > 220)
{
validationResult = false;
messages.Add("A player must be most 220 cm tall to be registered");
}
if (request.Weight.HasValue && request.Weight.Value < 40)
{
validationResult = false;
messages.Add("A player must be at least 40 kg to be registered");
}
if (request.Weight.HasValue && request.Weight.Value > 140)
{
validationResult = false;
messages.Add("A player must be most 140 kg tall to be registered");
}
return new ValidationResult
{
IsValidated = validationResult,
Messages = messages
};
}
}
}
请注意,我们已将此CreatePlayerValidator类的访问修饰符从public更改为internal。由于我们将仅在当前程序集中使用此类,因此这是最适合设置的访问修饰符。另外,标记我们正在覆盖从基类继承的Validate方法,但仍然不想丢失在基类中完成的验证过程。为了保持基础验证,我们首先从基类运行验证过程,如果基础验证失败,则返回而不进一步执行。尽管这不是强制性的(开发人员可以选择一次返回所有验证结果),但这是一种设计选择。如果我们继续进行基本验证过程,下一步是通过使用恢复原始请求as关键词。由于我们已将输入参数定义为BaseRequest, 以增加可重用性,所有其他请求对象都是从其中派生的,因此可以将所有请求对象发送到此方法。所以为了保证这个过程的安全,我们应该首先检查类型并将其分配给它的原始类型以检查类型验证,然后访问请求对象的属性。然后我们创建两个属性validationResult和messages,它们与ValidatonResult类属性相同,其余的本身就很简单。首先,如有必要则检查是否为空或null,然后根据业务规则检查值。在这个例子中,我们要求年龄超过10岁,身高在100到220厘米之间,体重在40到140公斤之间。这些只是业务规则,它们不是一成不变的,如果不需要,甚至可能不存在。最后,我们所要做的就是创建一个ValidationResult类型的返回对象并设置我们从规则检查中收集的值。
如有必要,相同的规则适用于所有请求对象。
internal class GetClubPlayersValidator : BaseValidator
{
public override ValidationResult Validate(BaseRequest req)
{
ValidationResult result = base.Validate(req);
if (!result.IsValidated)
return result;
else
{
bool validationResult = true;
List messages = new List();
GetClubPlayersRequest request = req as GetClubPlayersRequest;
if (request == null)
return new ValidationResult
{
IsValidated = false,
Messages = new List { "Can not convert request object
or request object null" }
};
if (string.IsNullOrWhiteSpace(request.Club))
{
validationResult = false;
messages.Add("Club name attribute can not be null
when searching for club players");
}
return new ValidationResult
{
IsValidated = validationResult,
Messages = messages
};
}
}
}
internal class GetPlayerByIdValidator : BaseValidator
{
public override ValidationResult Validate(BaseRequest req)
{
ValidationResult result = base.Validate(req);
if (!result.IsValidated)
return result;
else
{
bool validationResult = true;
List messages = new List();
GetPlayerByIdRequest request = req as GetPlayerByIdRequest;
if (request == null)
return new ValidationResult
{
IsValidated = false,
Messages = new List { "Can not convert request object
or request object null" }
};
if (request.PlayerId < 1)
{
validationResult = false;
messages.Add("Player identifier should be a positive integer");
}
return new ValidationResult
{
IsValidated = validationResult,
Messages = messages
};
}
}
}
在创建必要的验证类之后,我们应该更新我们的服务方法以包含验证过程。但在继续之前,如果您还没有注意到,请快速提醒一下,我们还没有创建一个名为GetAllPlayersValidator的验证器类以验证GetAllPlayers方法。由于该方法只需要一个BaseRequest类型的输入对象,因此BaseValidator类足以验证该方法的传入请求。
这些更改将使我们的服务方法如下所示:
public CreatePlayerResponse CreatePlayer(CreatePlayerRequest request)
{
try
{
CreatePlayerValidator validator = new CreatePlayerValidator();
ValidationResult valResult = validator.Validate(request);
if (!valResult.IsValidated)
return new CreatePlayerResponse
{
IsException = false,
IsSuccess = false,
Messages = valResult.Messages.ToArray()
};
return new CreatePlayerResponse
{
PlayerId = PlayerRepository.CreateNewPlayer(request.Name,
request.DateOfBirth, request.Height, request.Weight, request.Club),
IsException = false,
IsSuccess = true,
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
{
BaseValidator validator = new BaseValidator();
ValidationResult valResult = validator.Validate(request);
if (!valResult.IsValidated)
return new GetAllPlayersResponse
{
IsException = false,
IsSuccess = false,
Messages = valResult.Messages.ToArray()
};
return new GetAllPlayersResponse
{
PlayerList = PlayerRepository.GetAllPlayers(),
IsException = false,
IsSuccess = true,
Messages = new string[] { "Operation successful" }
};
}
catch (Exception ex)
{
return new GetAllPlayersResponse
{
IsException = true,
IsSuccess = false,
Messages = new string[] { ex.Message }
};
}
}
public GetClubPlayersResponse GetClubPlayers(GetClubPlayersRequest request)
{
try
{
GetClubPlayersValidator validator = new GetClubPlayersValidator();
ValidationResult valResult = validator.Validate(request);
if (!valResult.IsValidated)
return new GetClubPlayersResponse
{
IsException = false,
IsSuccess = false,
Messages = valResult.Messages.ToArray()
};
return new GetClubPlayersResponse
{
PlayerList = PlayerRepository.GetClubPlayers(request.Club),
IsException = false,
IsSuccess = true,
Messages = new string[] { "Operation successful" }
};
}
catch (Exception ex)
{
return new GetClubPlayersResponse
{
IsException = true,
IsSuccess = false,
Messages = new string[] { ex.Message }
};
}
}
public GetPlayerByIdResponse GetPlayerById(GetPlayerByIdRequest request)
{
try
{
GetPlayerByIdValidator validator = new GetPlayerByIdValidator();
ValidationResult valResult = validator.Validate(request);
if (!valResult.IsValidated)
return new GetPlayerByIdResponse
{
IsException = false,
IsSuccess = false,
Messages = valResult.Messages.ToArray()
};
return new GetPlayerByIdResponse
{
Player = PlayerRepository.GetPlayerById(request.PlayerId),
IsException = false,
IsSuccess = true,
Messages = new string[] { "Operation successful" }
};
}
catch (Exception ex)
{
return new GetPlayerByIdResponse
{
IsException = true,
IsSuccess = false,
Messages = new string[] { ex.Message }
};
}
}
这是我们系列的第二部分,解释了将验证过程添加到我们的服务请求中。
您可以在此处阅读下一部分(身份验证/授权)。
https://www.codeproject.com/Articles/5325807/Improving-WCF-Service-Quality-Part-2-Validation