C# 控制器参数不会变为null
我遇到了以下问题: 我有一个asp.net mvc 5控制器,其参数为引用类型:C# 控制器参数不会变为null,c#,asp.net-mvc,controller,C#,Asp.net Mvc,Controller,我遇到了以下问题: 我有一个asp.net mvc 5控制器,其参数为引用类型: [Route("")] [HttpGet] public ActionResult GetFeeds(Location location) { if (location == null) { // init location or smth } // Do smth with location
[Route("")]
[HttpGet]
public ActionResult GetFeeds(Location location)
{
if (location == null)
{
// init location or smth
}
// Do smth with location
return new EmptyResult();
}
您会注意到我正在使用AttributeRouting。没有具有此操作名称的其他方法
但是-这是我的位置类:
public class Location : ILocation
{
public DbGeography Coordinates { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
这里没有什么特别之处(接口定义了所有这些属性)
如果我正在访问控制器操作(实际上使用powershell)并传递以下内容:
http://localhost:2000/Feed?latitude=23.1&longitude=37
一切正常,但如果我使用
http://localhost:2000/Feed
location参数不为null(它是一个具有默认值的新位置),这是我想要的行为:(
有人知道为什么会这样吗
提前感谢MVC模型绑定器已经接管。我最初发布的内容将在不通过模型绑定器的实例中起作用。但是,通过我自己的快速测试,由于绑定器的工作方式,viewmodel参数似乎永远不会为空,并将属性与表单值联系起来 在您的实例中,我将检查纬度和经度是否都为null,以查看是否未传递任何内容。这意味着您需要在ViewModel上使它们为null
public class Location : ILocation
{
public DbGeography Coordinates { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
}
更新控制器代码
if (location.Latitude == null && location.Longitude == null)
{
// init location or smth
}
ModelBinder创建对象的新实例,因此您在此处有两个选项: 在“必需属性”上有一个
[必需的]
数据批注,并将它们标记为可为空,然后检查ModelState.IsValid
(推荐)
将纬度和经度设为空double?
您可以检查Latitude.HasValue&&longide.HasValue
更新:
public class Location : ILocation
{
public DbGeography Coordinates { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
}
public class LocationGetFeedsViewModel : LocationGetFeedsBinderModel {
// change coordinates to string because maybe that's easier to handle on the view.
public string Coordinates { get; set; }
// added to sum to the example
public IEnumerable<SelectListItem> Zones { get; set; }
}
public class LocationGetFeedsBinderModel {
[Required]
public double? Latitude { get; set; }
[Required]
public double? Longitude { get; set; }
}
public ActionResult GetFeeds(LocationGetFeedsBinderModel location) {
if (!ModelState.IsValid)
// redirect or display some error
return new EmptyResult();
}
默认的模型绑定器将始终实例化一个复杂对象,因此不幸的是,它永远不会为null,并且将跳过任何可选的赋值 这种行为应用于您的系统的最终结果是,您必须检测默认构造函数的使用。反射或隐式检查无法做到这一点 必须明确地做到这一点 它可以通过在默认构造函数中设置类中的标志、接受答案中建议的可为空的属性、使用Bart建议的数据注释、使用属性的自定义get和set方法或各种其他方式来实现。我一直在测试使用angularjs发布数据。 我发现,如果强制将值设置为null而不是undefined,ModelBinder不会实例化复杂对象
if (!location)
location = null;
$http({ method: "POST", url: "/Location/Create", data: { location } })
活页夹按设计工作-它实例化一个
位置
对象,并用默认值填充其值,除非您提供覆盖值(第二个示例中没有)。它不会仅仅因为路由未提供任何值而创建位置对象的空实例。@ChristopherKellner-由于“模型绑定器如何工作”,它似乎不可能出现行得通。请看我答案中的链接,了解更多的解释。我尝试了这个方法,但位置仍然是位置的新实例。还有什么建议吗?@ChristopherKellner-是的,你说得对,更新了我对youlocation.Latitude.HasValue的答案会更好看:DIt很奇怪,它不会映射为null。如果你标记为它是可空的?(Location?Location=null)
将特定于模型的验证放在控制器中是一个坏主意(控制器很难测试,而且很可能会重复自己的测试).ASP.Net MVC附带数据注释验证器有一个很好的理由。这是我最初的答案。当您进入ActionResult时,变量仍然是非空的。我在VS2013上测试了自己。猜测在MVC中的某个点对DefaultModelBinder进行了更新。@Tommy-我删除了使用可选属性集t的建议o null。正如您准确声明的那样,它将被实例化,并且永远不会分配可选值。请查看编辑。遗憾的是,我无法将这些属性更改为可空值,因为它们是由ILocation继承的。我看到了实现目标的3种可能性:创建一个具有可空属性的新VM类,并将它们解析到新位置n或检查它们是否都为0,因为这是非常罕见的。两者都感觉不正确:/第三个可以为空,作为参数,这似乎更好。或者你有其他想法吗?这就是我所说的BinderModel
而不是ViewModel。这是非常有用的,因为ViewModel通常比只接受2或3。两种方法都是最好的。事实上,我认为有一个控制器来接受全视图模型是很少见的,甚至是很糟糕的。所以你会推荐使用“BinderModel”的第一种方法?是的,是的,是的。当然。在真实的word场景中,当您只有一个表单时,我会让ViewModel继承BinderModel,如:LocationEditViewModel:LocationEditBinderModel。您将可以使用完全有效的数据批注访问视图上的所有属性,并且控制器级别的Post签名将收到LocationEditBinderModel。我为所有POST都这样做。对我来说,完全控制暴露和可供用户使用的属性的数量是非常重要的,特别是为了避免在模型级别上过度伪装和保持控制器签名干净。在您的示例中,我可以发布坐标属性,我认为这不是有意的,所以您要公开它er发帖。这可能是安全的,也可能是糟糕的。最好是通过设计来防止。我在2020年使用React,效果非常好。