iprincipal_ASP.NET MVC中的IPrincipal(用户)ModelBinder,可简化测试

iprincipal

iprincipal

ModelBinders are great. I've blogged a bit about them before like the File Upload Model Binder.

ModelBinders很棒。 像File Upload Model Binder之前,我已经写过一些关于它们的文章。

I am working on some code like this:

我正在像这样的一些代码:

[Authorize]
public ActionResult Edit(int id) {

Dinner dinner = dinnerRepository.FindDinner(id);

if (dinner.HostedBy != User.Identity.Name)
return View("InvalidOwner");

var viewModel = new DinnerFormViewModel {
Dinner = dinner,
Countries = new SelectList(PhoneValidator.Countries, dinner.Country)
};

return View(viewModel);
}

It's pretty straight forward, but this Controller knows too much. It's reaching into implicit parameters. The id was passed in, but the User is actually a property of the Controller base class and ultimately requires an HttpContext. Having this method "know" about the User object, and worse yet, having the User object go reaching into HttpContext.Current makes this hard to test.

这很简单,但是这个Controller知道的太多了。 它涉及隐式参数。 传入了id,但是User实际上是Controller基类的属性,最终需要HttpContext。 使这种方法“了解” User对象,更糟糕的是,使User对象进入HttpContext.Current使这很难测试。

I'd like to have the convenience of passing in the User (actually an IPrincipal interface) when I want to test, but when I'm running the app, I'd like to have the IPrincipal get passed into my method automatically. Enter the Model Binder. I need to teach ASP.NET MVC what to do when it sees a type as a parameter.

我想在进行测试时方便地传入User(实际上是IPrincipal接口),但是当我运行应用程序时,我想让IPrincipal自动传递到我的方法中。 输入模型活页夹。 我需要教ASP.NET MVC将类型视为参数时该怎么做。

This quickie model binder is now responsible for one thing - it knows how to reach down into the HttpContext and get the current User (IPrincipal). It has one single responsibility.

这种快速模型绑定器现在负责一件事-它知道如何深入到HttpContext并获取当前用户(IPrincipal)。 它只有一项责任。

public class IPrincipalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
if (bindingContext == null) {
throw new ArgumentNullException("bindingContext");
}
IPrincipal p = controllerContext.HttpContext.User;
return p;
}
}

Now I can release the Controller from the emotional baggage of knowing too much about the User object. It can just have that passed in automatically by the framework. I just need to register the binder to tell folks about it. I can either do it on a one-off basis and put an attribute on this one method parameter:

现在,我可以从对用户对象了解太多的情感包中释放控制器。 它可以只是由框架自动传递。 我只需要注册活页夹就可以告诉人们。 我可以一次性完成此操作,然后将属性放在此一个方法参数上:

public ActionResult Edit(int id,                        
[ModelBinder(typeof(IPrincipalModelBinder))]
IPrincipal user)
{...}

But even better, I can just tell the whole application once in the global.asax:

但更好的是,我只需要在global.asax中告诉整个应用程序一次即可:

void Application_Start() {
RegisterRoutes(RouteTable.Routes); //unrelated, don't sweat this line.
ModelBinders.Binders[typeof(IPrincipal)] = new IPrincipalModelBinder();
}

Now that ASP.NET MVC knows what to do when it see an IPrincipal as a method parameter, my method gets nicer.

既然ASP.NET MVC知道将IPrincipal作为方法参数时该怎么办,我的方法就变得更好了。

[Authorize]
public ActionResult Edit(int id, IPrincipal user) {

Dinner dinner = dinnerRepository.FindDinner(id);

if (dinner.HostedBy != user.Identity.Name)
return View("InvalidOwner");

var viewModel = new DinnerFormViewModel {
Dinner = dinner,
Countries = new SelectList(PhoneValidator.Countries, dinner.Country)
};

return View(viewModel);
}

Now I can test my controller more easily by passing in fake users. No need for mocking in this case!

现在,我可以通过传入假用户来更轻松地测试我的控制器。 在这种情况下,无需嘲笑!

[TestMethod]
public void EditAllowsUsersToEditDinnersTheyOwn()
{
// Arrange
DinnersController controller = new DinnersController(new TestDinnerRespository());

// Act
IPrincipal FakeUser = new GenericPrincipal(new GenericIdentity("Scott","Forms"),null);
ViewResult result = controller.Edit(4, FakeUser) as ViewResult;

// Yada yada yada assert etc etc etc
Assert.IsTrue(result.ViewName != "InvalidOwner");
}

Fun stuff.

好玩的东西。

UPDATE: Phil had an interesting idea. He said, why not make method overloads, one for testing and one for without. I can see how this might be controversial, but it's very pragmatic.

更新:菲尔有一个有趣的主意。 他说,为什么不使方法重载,一种方法用于测试,另一种方法用于无方法。 我可以看到这可能会引起争议,但这非常实用。

[Authorize]
public ActionResult Edit(int id)
{
return Edit(id, User); //This one uses HttpContext
}

You'd use this one as before at runtime, and call the overload that takes the IPrincipal explicitly for testing.

您将在运行时像以前一样使用它,并调用将IPrincipal明确用于测试的重载。

Yes, I realize I could use an IoC container for this also.

是的,我知道我也可以为此使用IoC容器。

翻译自: https://www.hanselman.com/blog/iprincipal-user-modelbinder-in-aspnet-mvc-for-easier-testing

iprincipal

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值