【问题标题】:How to validate type of collections?如何验证集合的类型?
【发布时间】:2019-04-22 06:44:14
【问题描述】:

如何验证和捕获 System.Web.Http.ApiController 类的集合类型转换(JSON 字符串数组到 C# 长集合)(如果可能,在模型初始化之前)?

我想验证并捕获 JSON 数组中的任何非数字元素,以作为错误的请求响应返回(可能以某种方式带有数据注释)。

当包含非数字 JSON 元素(要转换为长集合)时,它们无法解析并在模型传递给 ApiController 方法之前被剥离。鉴于以下类,有效输入应仅包含“PerferredNutTypes”和“GeographyIDs”的数值。

public class SquirrelController : ApiController
{
    [HttpPost]
    [Route("api/squirrels/search")]
    [SwaggerResponse(HttpStatusCode.OK, Type = typeof(SquirrelsResponse))]
    public HttpResponseMessage Squirrels(SquirrelsRequest model)
    {
        // model already parsed by the time breakpoint reaches here and non-convertable elements already stripped

        ...
        ...
        ...

        SquirrelsResponse results = Targeting.SearchForSquirrels(model);
        return Request.CreateResponse(HttpStatusCode.OK, results);
    }
}

public class SquirrelsRequest
{
    public SquirrelsRequest() {}

    public List<long> PreferredNutTypes { get; set; } = new List<long>();
    public GeographySearch geographySearch { get; set; } = new GeographySearch();
}

public class GeographySearch
{
    public GeographySearch() {}

    public BooleanOperator Operator { get; set; } = BooleanOperator.OR;
    public List<long> GeographyIDs { get; set; } = new List<long>();
}

public enum BooleanOperator
{
    AND,
    OR
}

示例:

//"Toronto" sould be an invalid input when converting from JSON string array to c# long collection.
{
  "PreferredNutTypes": [34,21],
  "GeographySearch": {
    "Operator": 1,
    "GeographyIDs": ["Toronto"]
  },
}

// This is what the model currently looks like in public HttpResponseMessage Squirrels(SquirrelsRequest model)
new SquirrelsRequest()
{
    PreferredNutTypes = new List<long>() { 34, 21 },
    GeographySearch = new GeographySearch()
    {
        Operator = 1
        GeographyIDs = new List<long>()
    }
}

期望:

  • 理想情况下捕获任何非数字值并将它们作为错误请求返回。类似于如何验证到如何使用数据注释来验证范围。
  • 可以接受数据注释解决方案。
  • 理想情况下,这是一种比访问 ModelState 和解析错误消息/密钥更清晰的方法。
  • 理想情况下,可以普遍应用于任何集合。
  • 我尝试过的事情:

  • 尝试了自定义数据注释验证器,但只能在解析后访问值。
  • 尝试通过 HttpActionContext 的 ModelState 访问验证错误,但充其量只能获取这些值...
  • System.Web.Http.Controllers.HttpActionContext actionContext.ModelState.["model.GeographySearch.GeographyIDs[0]"].Errors[0].Exception.Message => "Error converting value \"sonali7678687\" to type 'System.Int64'. Path 'subjectSearch.writingAbout[0]', line 6, position 36."
    System.Web.Http.Controllers.HttpActionContext actionContext.ModelState.["model.GeographySearch.GeographyIDs[0]"].Errors[0].Exception.InnerException.Message => "Input string was not in a correct format."
    

    ...肯定有更好的验证方式吗?

    更新 1: 改写问题以使解释和意图更清楚。

    【问题讨论】:

    • ModelState 错误告诉你想要是无效的(即在你给出的例子中,它告诉你GeographyIDs 的第一个值是无效的)所以它不清楚你在期待什么或想做。
    • 我认为您可以从Request["GeographySearch.GeographyId"] 获取当前值,但如果结果不为空,我将只查看ModelState.Where(m =&gt; m.Errors.Count &gt; 0) 并返回通用Response.StatusCode = 404 以保持简单。跨度>

    标签: c# asp.net asp.net-mvc data-annotations asp.net-apicontroller


    【解决方案1】:

    为什么你的字体很长?另外,你会使用小数吗?如果没有,您需要的是 int.TryParse()。无论您在哪里使用 int.parse(或您的情况下的 long.parse()),都将其替换为 int.TryParse()。 TryParse 返回一个布尔值(True 或 False),让您知道要解析的字符串是否为数字。

    示例:

    bool isNumber = int.TryParse("Data I'm Trying to parse but it's a string", variableTheResultWillGetStoredToifSuccessful);
    

    这将返回错误,并且我永远不应该使用的超长变量名将保持为空或不变。因此,您可以这样做

    if(isNumber == false){
    //skip storing the number
    }else{
    //keep doing what you're doing;
    }
    

    你也可以这样做,更简洁地重写上面的内容:

    if(isNumber){
    //keep doing what you're doing
    }
    

    或多或少,这就是它的摘要。 TryParse 将根据解析是否成功返回 true 或 false 而不会导致代码崩溃,然后您可以使用该信息继续您看起来合适的方式。

    如果您要使用小数,请使用 double 而不是 int。

    【讨论】:

      【解决方案2】:

      您可以尝试 JSON Schema 验证器,修改您的方法以接收 JSON 正文,先对其进行验证,然后将其转换为模型。

      public class SquirrelController : ApiController
      {
          [HttpPost]
          [Route("api/squirrels/search")]
          public SquirrelsResponse Squirrels(PostBody model)
          {
              var generator = new JSchemaGenerator();
              var schema = generator.Generate(typeof(SquirrelsRequest));
              var body = JObject.Parse(model.Body);
      
              bool valid = body.IsValid(schema, out IList<string> messages);
              if (!valid)
              {
                  // Fail, do something
              }
      
              // Success
          }
      }
      
      public class PostBody
      {
          public string Body { get; set; }
      }
      

      获取更多信息...
      Validating JSON with JSON Schema
      Json.NET Schema

      【讨论】:

      • 你不能这样验证它,因为如果它是无效的消息将是空的
      • @Marc 如果你只是像我说的那样接受请求作为字符串,而不是模型,它不会无效。
      • 对不起,我错过了那部分,但后来他改变了 api。客户看到一个字符串而不是一个长列表会感到困惑
      猜你喜欢
      • 2021-05-17
      • 1970-01-01
      • 2020-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      • 1970-01-01
      • 2012-05-30
      相关资源
      最近更新 更多