C# +代码合约 - 抛出异常
更新 - 由于缺乏我方面的解释,我重写了这篇文章。
您对使用代码契约在无效输入上引发异常有何看法? (我正在根据我的服务合同进行编码,该合同要求用户名不能为空或包含空格)
MembershipServiceContracts.cs - 位于子文件夹的服务层
[ContractClassFor(typeof (IMemberShipService))]
internal abstract class MemberShipServiceContracts : IMemberShipService
{
#region IMemberShipService Members
public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
Contract.Requires(!String.IsNullOrWhiteSpace(userName), "Test");
Contract.Requires(!String.IsNullOrWhiteSpace(password));
Contract.Requires(!String.IsNullOrWhiteSpace(email));
return default(MembershipCreateStatus);
}
#endregion
}
MembershipService.cs - 位于在我的服务层中
[ContractClass(typeof (MemberShipServiceContracts))]
public interface IMemberShipService
{
MembershipCreateStatus CreateUser(string userName, string password, string email);
}
public class MemberShipService : IMemberShipService
{
private readonly MembershipProvider _provider;
public MemberShipService()
: this(null)
{ }
public MemberShipService(MembershipProvider provider)
{
_provider = provider ?? Membership.Provider;
}
#region IMemberShipService Members
public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
MembershipCreateStatus status;
_provider.CreateUser(userName, password, email, null, null, true, null, out status);
return status;
}
#endregion
}
AccountController.cs - 位于 UI 层
现在这是有趣的部分...
如果
[Authorize(Roles = "Developer")]
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
if (!String.IsNullOrEmpty(model.UserName))
{
throw new ArgumentException("UserName May not be null or contain only white spaces.", model.UserName);
}
if (!String.IsNullOrEmpty(model.Password))
{
throw new ArgumentException("Password May not be null or contain only white spaces", model.Password);
}
if (!String.IsNullOrEmpty(model.Email))
{
throw new ArgumentException("Email May not be null or contain only white spaces", model.Email);
}
if (!ModelState.IsValid)
{
return Json("Model validation failed");
}
MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
model.Email);
return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
}
为
[Authorize(Roles = "Developer")]
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.UserName),
"UserName May not be null or contain only white spaces.");
Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Password),
"Password May not be null or contain only white spaces");
Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Email),
"Email May not be null or contain only white spaces");
if (!ModelState.IsValid)
{
return Json("Model validation failed");
}
MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
model.Email);
return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
}
代码收缩 CreateUser() 方法未实现?
提前致谢。
UPDATE - Due to lack of explanation from my side, I've rewritten the post.
What do you think about using Code Contracts to throw exceptions on invalid input?
(I'm coding against a contract for my Service which requires the UserName not to be null or contain whitespaces)
MembershipServiceContracts.cs - Located in the service layer in a subfolder
[ContractClassFor(typeof (IMemberShipService))]
internal abstract class MemberShipServiceContracts : IMemberShipService
{
#region IMemberShipService Members
public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
Contract.Requires(!String.IsNullOrWhiteSpace(userName), "Test");
Contract.Requires(!String.IsNullOrWhiteSpace(password));
Contract.Requires(!String.IsNullOrWhiteSpace(email));
return default(MembershipCreateStatus);
}
#endregion
}
MembershipService.cs - Located in my service layer
[ContractClass(typeof (MemberShipServiceContracts))]
public interface IMemberShipService
{
MembershipCreateStatus CreateUser(string userName, string password, string email);
}
public class MemberShipService : IMemberShipService
{
private readonly MembershipProvider _provider;
public MemberShipService()
: this(null)
{ }
public MemberShipService(MembershipProvider provider)
{
_provider = provider ?? Membership.Provider;
}
#region IMemberShipService Members
public MembershipCreateStatus CreateUser(string userName, string password, string email)
{
MembershipCreateStatus status;
_provider.CreateUser(userName, password, email, null, null, true, null, out status);
return status;
}
#endregion
}
AccountController.cs - located at the UI layer
Now this is the interesting part...
Should I use:
[Authorize(Roles = "Developer")]
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
if (!String.IsNullOrEmpty(model.UserName))
{
throw new ArgumentException("UserName May not be null or contain only white spaces.", model.UserName);
}
if (!String.IsNullOrEmpty(model.Password))
{
throw new ArgumentException("Password May not be null or contain only white spaces", model.Password);
}
if (!String.IsNullOrEmpty(model.Email))
{
throw new ArgumentException("Email May not be null or contain only white spaces", model.Email);
}
if (!ModelState.IsValid)
{
return Json("Model validation failed");
}
MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
model.Email);
return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
}
or:
[Authorize(Roles = "Developer")]
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.UserName),
"UserName May not be null or contain only white spaces.");
Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Password),
"Password May not be null or contain only white spaces");
Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Email),
"Email May not be null or contain only white spaces");
if (!ModelState.IsValid)
{
return Json("Model validation failed");
}
MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
model.Email);
return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
}
to throw the an exception if the code contracts for the CreateUser() method isnt fulfilled?
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
代码合同中的失败表明您的代码存在需要修复的严重错误。它不会取代用户输入的验证。相反,您将首先验证用户输入,然后在确定有效时尝试处理数据(否则提示用户重新输入)。如果您的有效数据不满足目标合约,那么将会抛出一个无法捕获的异常,因为您显然有一个错误。
我希望这是一个灵丹妙药,但很快意识到我仍然需要我的验证。从那以后,我开始真正喜欢代码合约取代我常用的所有防护代码的方式。
想一想您每次都会遇到 NullReferenceException(对我来说太多了!)。代码契约可以消除这种痛苦。
R.
Ps 另外,不要使用合约来验证安全敏感数据,因为代码合约可以在运行时关闭......
A failure in a Code Contract indicates that your code has a serious bug that should be fixed. It doesn't replace validation of user input. Rather, you would first validate your user input, then attempt to process the data if determined to be valid (otherwise prompt the user to re-enter). If you then don't satisfy the target contract with your valid data, then an uncatchable exception will be thrown as you pretty obviously have a bug.
I hoped it would be a silver bullet but quickly realised I still needed my validation. After that, I grew to really love the way code contracts will replace all of my usual guard code.
Think of all the times you would get a NullReferenceException (too many for me!). Code Contracts can take that pain all away.
R.
P.s. Also, don't use contracts to validate security-sensitive data as code contracts can be turned off at runtime...
恐怕要说:这取决于你想如何使用代码契约。
您最好查看用户手册,部分“使用指南”。这是一小段摘录:
我建议您在做出任何重大决定之前阅读整个部分。
I'm afraid to say: it depends on how you want to use code contracts.
You'd better look at the user manual, section "Usage Guidelines". Here's a small excerpt:
I'd suggest you read the whole section before making any big decisions.
两种方法都很好。
检查先决条件的 3 种方法是
Contract.Requires -> 。进入该方法时,特定条件必须为真。
合同.需要->与上面相同,但如果不满足条件则抛出异常
Requires 始终被编译,因此使用此方法需要对工具产生硬依赖。在使用此方法之前,您应该决定是否需要这样做。
你的第一个方法 -->这种类型的前提条件的好处是它总是可以执行运行时检查。
在第一种方法中,您可能需要添加
Contract.EndContractBlock();
我自己正在探索此功能,但我认为您可以在服务层中抛出异常,这是可以的。
华泰
Both of the approaches are fine .
The 3 ways that one has for checking pre conditions are
Contract.Requires -> a particular condition must be true upon entry to the method.
Contract.Requires -> Same as above but throw exception if condition not met
Requires is always compiled, so use of this method entails a hard dependency on the tools. You should decide if you want that before using this method.
Your first approach --> The benefit of this type of precondition is that it is always there to perform the runtime check.
In the first approach you might want to add
Contract.EndContractBlock();
I am exploring this feature myself but I think you can throw exception in your service layer and it is ok.
HTH