【问题标题】:binding a Guid parameter in asp.net mvc core在 asp.net mvc core 中绑定 Guid 参数
【发布时间】:2017-02-24 23:16:50
【问题描述】:

我想将 Guid 参数绑定到我的 ASP.NET MVC Core API:

[FromHeader] Guid id

但它始终为空。如果我将参数更改为字符串并手动从字符串中解析 Guid,它可以工作,所以我认为它没有将 Guid 检测为可转换类型。

the documentation 它说

在 MVC 中,简单类型是任何 .NET 原始类型或带有字符串类型转换器的类型。

Guids (GuidConverter) 有一个类型转换器,但可能 ASP.NET MVC Core 不知道它。

有谁知道如何将 Guid 参数与 ASP.NET MVC Core 绑定或如何告诉它使用 GuidConverter?

【问题讨论】:

  • 使用 FromBody 属性怎么样?
  • 我正在使用 FromHeader 属性,因为我想要的值在标题而不是正文中
  • 澄清一下,这似乎是[FromHeader] 绑定源特有的问题。我可以正确绑定来自查询字符串和正文的 Guid。

标签: c# asp.net-core asp.net-core-mvc


【解决方案1】:

我刚刚发现基本上 ASP Core 只支持将标头值绑定到字符串和字符串集合! (而路由值、查询字符串和正文的绑定支持任何复杂类型)

您可以查看HeaderModelBinderProvider source in Github 并亲自查看:

public IModelBinder GetBinder(ModelBinderProviderContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException(nameof(context));
    }

    if (context.BindingInfo.BindingSource != null &&
            context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Header))
    {
        // We only support strings and collections of strings. Some cases can fail
        // at runtime due to collections we can't modify.
        if (context.Metadata.ModelType == typeof(string) ||
            context.Metadata.ElementType == typeof(string))
        {
            return new HeaderModelBinder();
        }
    }

    return null;
}

我已经提交了new issue,但同时我建议您绑定到字符串或创建自己的特定模型绑定器(将[FromHeader][ModelBinder] 组合到您自己的绑定器中)


编辑

示例模型绑定器可能如下所示:

public class GuidHeaderModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(Guid)) return Task.CompletedTask;
        if (!bindingContext.BindingSource.CanAcceptDataFrom(BindingSource.Header)) return Task.CompletedTask;

        var headerName = bindingContext.ModelName;
        var stringValue = bindingContext.HttpContext.Request.Headers[headerName];
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, stringValue, stringValue);

        // Attempt to parse the guid                
        if (Guid.TryParse(stringValue, out var valueAsGuid))
        {
            bindingContext.Result = ModelBindingResult.Success(valueAsGuid);
        }

        return Task.CompletedTask;
    }
}

这是一个使用它的例子:

public IActionResult SampleAction(
    [FromHeader(Name = "my-guid")][ModelBinder(BinderType = typeof(GuidHeaderModelBinder))]Guid foo)
{
    return Json(new { foo });
}

您可以尝试一下,例如在浏览器中使用 jquery:

$.ajax({
  method: 'GET',
  headers: { 'my-guid': '70e9dfda-4982-4b88-96f9-d7d284a10cb4' }, 
  url: '/home/sampleaction'
});

【讨论】:

    【解决方案2】:

    [更新]

    这已在 2.1.0-preview2 中得到改进。您的代码现在实际上可以工作。您可以将标头中的非字符串类型绑定到您的参数。您只需要在您的启动类中设置兼容版本即可。

    控制器

    [HttpGet]
    public Task<JsonResult> Get([FromHeader] Guid id)
    {
        return new JsonResult(new {id});
    }
    

    启动

    Services
      .AddMvc
      .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    

    看看上面提到的同一个 Github 讨论: https://github.com/aspnet/Mvc/issues/5859

    【讨论】:

      【解决方案3】:

      我是这样做的,它不需要控制器动作的附加属性。

      模型绑定器

      public class GuidHeaderModelBinder : IModelBinder
      {
          public async Task BindModelAsync(ModelBindingContext BindingContext)
          {
              // Read HTTP header.
              string headerName = BindingContext.FieldName;
              if (BindingContext.HttpContext.Request.Headers.ContainsKey(headerName))
              {
                  StringValues headerValues = BindingContext.HttpContext.Request.Headers[headerName];
                  if (headerValues == StringValues.Empty)
                  {
                      // Value not found in HTTP header.  Substitute empty GUID.
                      BindingContext.ModelState.SetModelValue(BindingContext.FieldName, headerValues, Guid.Empty.ToString());
                      BindingContext.Result = ModelBindingResult.Success(Guid.Empty);
                  }
                  else
                  {
                      // Value found in HTTP header.
                      string correlationIdText = headerValues[0];
                      BindingContext.ModelState.SetModelValue(BindingContext.FieldName, headerValues, correlationIdText);
                      // Parse GUID.
                      BindingContext.Result = Guid.TryParse(correlationIdText, out Guid correlationId)
                          ? ModelBindingResult.Success(correlationId)
                          : ModelBindingResult.Failed();
                  }
              }
              else
              {
                  // HTTP header not found.
                  BindingContext.Result = ModelBindingResult.Failed();
              }
              await Task.FromResult(default(object));
          }
      }
      

      Model Binder Provider(验证模型绑定成功的条件)

      public class GuidHeaderModelBinderProvider : IModelBinderProvider
      {
          public IModelBinder GetBinder(ModelBinderProviderContext Context)
          {
              if (Context.Metadata.ModelType == typeof(Guid))
              {
                  if (Context.BindingInfo.BindingSource == BindingSource.Header)
                  {
                      return new BinderTypeModelBinder(typeof(GuidHeaderModelBinder));
                  }
              }
              return null;
          }
      }
      

      FooBar 控制器操作

      [HttpGet("getbars")]
      public async Task<string> GetBarsAsync([FromHeader] Guid CorrelationId, int Count)
      {
          Logger.Log(CorrelationId, $"Creating {Count} foo bars.");
          StringBuilder stringBuilder = new StringBuilder();
          for (int count = 0; count < Count; count++)
          {
              stringBuilder.Append("Bar! ");
          }
          return await Task.FromResult(stringBuilder.ToString());
      }
      

      启动

      // Add MVC and configure model binding.
      Services.AddMvc(Options =>
      {
          Options.ModelBinderProviders.Insert(0, new GuidHeaderModelBinderProvider());
      });
      

      【讨论】:

        【解决方案4】:

        执行此操作的最简单方法是在控制器操作中删除 Guid 类型参数之前的属性,如下所示:

        public async Task<IActionResult> UpdateAsync(Guid ApplicantId, [FromBody]UpdateApplicantRequest request) {}

        简单明了,希望对你有帮助。

        【讨论】:

          猜你喜欢
          • 2021-05-20
          • 1970-01-01
          • 2021-10-09
          • 1970-01-01
          • 2018-04-27
          • 1970-01-01
          • 2020-11-05
          • 2010-10-13
          • 1970-01-01
          相关资源
          最近更新 更多