【问题标题】:Web API model validation and default valuesWeb API 模型验证和默认值
【发布时间】:2015-05-27 12:26:37
【问题描述】:

这是我之前的问题Web API attribute routing and validation - possible? 的精神继承者,我认为这个问题太笼统了,无法回答。大多数问题都已解决,但默认值问题仍然存在。

基本上我已经解决了很多难题。我有这个:

[HttpGet]
[Route("test/{id}"]
public IHttpActionResult RunTest([FromUri]TestRequest request)
{
    if (!ModelState.IsValid) return BadRequest(ModelState);
    return Ok();
}

我的TestRequest 班级:

public class TestRequest
{
    public string id { get; set; }

    [DefaultValue("SomethingDefault")]
    public string something { get; set; }
}

问题在于,如果something 的查询字符串中没有参数,则模型是“有效的”,而somethingnull

如果我为something 指定一个空白值(即GET test/123?something=),那么默认值就会起作用,模型再次有效。

这是为什么?如何在此处将默认值添加到我的模型中?作为奖励,为什么不指定参数时,不使用默认值,而明确指定空字符串时,使用默认值?

(我一直在浏览 ASP.NET 堆栈源代码,并且对模型绑定器和绑定上下文非常了解。但我最好的猜测不可能是正确的 - 看起来 DefaultValueAttribute 仅在以下情况下使用参数值为null。但这里不是这样)

【问题讨论】:

  • 不是批评的意思,但是您是否在传递给外部各方之前自己使用此服务? (即吃你自己的狗粮?)。我一直觉得使用 ModelState 验证和 BadRequest 的简单返回对您的 API 用户(开发人员是这里的用户)完全没用,因为它完全不清楚出了什么问题。拥有一个规则引擎并在 badRequest 的主体中传回有用的错误消息要好得多,模型状态远没有帮助。这同样适用于在您的其他问题中进行验证的属性生根。
  • Macb - 是的,这是一个内部原型。我喜欢在这里利用 ModelState 的想法有几个原因:1)模型仅在“Web 层”中,因此可以具有特定于层的注释,2)请求上的注释是定义合同的好方法,3 ) 我们正在考虑为 Angular 编写客户端组件,它可以使用基于此的消息突出显示适当的字段,并且 4) 我也热衷于探索基于注释自动生成 API 文档。许多 if 语句在代码中隐藏了所有这些。属性路由对于“实际上是 RESTful”的 API 来说也很棒 :)
  • 对不起,我可能误解了 - 你说一个简单的回报是无用的和不清楚的。但是return BadRequest(ModelState) 为调用者提供了一个很好的 JSON 细分,说明了请求失败的原因以及导致它失败的原因——除非我误解了?
  • 我的语言有点强 :-) 这真的取决于您的 API 用户,从您的描述来看,听起来您正在构建内部使用/在您自己的外部网站上使用 - 其中案例模型验证很棒。就我个人而言,我喜欢对返回给用户的内容进行一点控制,即“为什么”这种失败的消息而不是通过模型验证获得的“这个”失败消息。例如,假设一个“电话”号码字段,很高兴被告知该号码与英国固定电话格式不匹配 - 而不是它无法匹配给定的正则表达式。
  • 添加到上面,我喜欢我的模型是可移植的。例如,我所有的模型都在一个可移植类库中,以允许在消费者(xamarin iOS/Android,Windows 商店应用程序)上重用模型,此外,我们将 dto 对象转换为带有type.litesolutions.net 的类型脚本,用于我们的淘汰赛web 应用程序,最大限度地重用 c# 模型。

标签: c# asp.net-web-api


【解决方案1】:

您需要在模型的构造函数中初始化默认值:

public class TestRequest
{
    public TestRequest()
    {
        this.something = "SomethingDefault";
    }

    public string id { get; set; }

    [DefaultValue("SomethingDefault")]
    public string something { get; set; }
}

更新:
使用 C# 6,您不再需要在构造函数中对其进行初始化。您可以直接为属性分配默认值:

public class TestRequest
{
    public string id { get; set; }

    [DefaultValue("SomethingDefault")]
    public string something { get; set; } = "SomethingDefault";
}

正如documentation of the DefaultValueAttribute 所说:

注意

DefaultValueAttribute 不会导致成员 使用属性的值自动初始化。您必须设置 代码中的初始值。

如果您没有为您的 something 属性提供任何值,则该属性将被初始化并且 ModelBinder 没有要分配给它的值,因此该属性默认为其默认值。

【讨论】:

  • 但它会自动填充我的属性——当我指定它应该是一个空白字符串时。我认为文档意味着属性本身不会这样做 - 但模型绑定器会这样做,但不是我期望的方式
  • (另外,这不起作用 - 你试过了吗,或者我没有尝试过?对我来说,当查询参数为空字符串时,它被覆盖为 null)跨度>
  • 抱歉,要明确一点:如果我使用构造函数来初始化属性,并完全删除 DefaultValue 属性,那么它几乎可以工作 - 缺少的参数给了我默认值。但是参数中的空白字符串会给出 null 。如果我保留 DefaultValue 属性,在两种情况下(null 和空白)我都会得到默认值 - 所以我不能指定空白值
  • 基本:构造函数参数指定一个值,如果没有传入参数; DefaultValue 指定传入空白字符串的值。因此,如果我将默认值放在构造函数中,并且[DefaultValue(string.Empty)],那么它会按我的预期工作。奇怪!
  • @KierenJohnstone 我认为这是预期的行为:当查询字符串指定一个空值时,something 属性的值应该为空。如果您想进一步控制它,那么我想您能做的最好的事情就是实现 something 属性的设置器。
【解决方案2】:

在构造函数中指定默认值适用于根本没有指定参数的情况,但是当指定一个空白字符串时,null 会被放入字段中。

因此,添加[DefaultValue("")] 实际上效果最好 - 当指定一个空白字符串时,传入一个空白字符串。然后构造函数可以指定参数缺失时的默认值。

为了解决这个问题,我创建了PreserveBlankStringAttribute,派生自DefaultValueAttribute,相当于[DefaultValue("")]

我非常欢迎比这更好的答案。

【讨论】:

    猜你喜欢
    • 2023-04-01
    • 2014-12-18
    • 1970-01-01
    • 2019-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多