C# 在asp.net mvc核心中绑定Guid参数

C# 在asp.net mvc核心中绑定Guid参数,c#,asp.net-core,asp.net-core-mvc,C#,Asp.net Core,Asp.net Core Mvc,我想将Guid参数绑定到我的ASP.NET MVC核心API: [FromHeader] Guid id 但它总是空的。如果我将参数更改为字符串并手动解析字符串中的Guid,它会工作,因此我认为它不会将Guid检测为可转换类型 里面说 在MVC中,简单类型是任何.NET基元类型或带有字符串类型转换器的类型 有一个用于Guids()的类型转换器,但可能ASP.NET MVC Core不知道 有人知道如何用ASP.NET MVC Core绑定Guid参数,或者如何告诉它使用GuidConverte

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

[FromHeader] Guid id
但它总是空的。如果我将参数更改为字符串并手动解析字符串中的Guid,它会工作,因此我认为它不会将Guid检测为可转换类型

里面说

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

有一个用于Guids()的类型转换器,但可能ASP.NET MVC Core不知道


有人知道如何用ASP.NET MVC Core绑定Guid参数,或者如何告诉它使用GuidConverter吗?

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

您可以查看
HeaderModelBinderProvider
并亲自查看:

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;
}
我已经提交了一个,但同时我建议您要么绑定到字符串,要么创建自己的特定模型绑定器(将
[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'
});

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

型号活页夹

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));
    }
}
[HttpGet]
public Task<JsonResult> Get([FromHeader] Guid id)
{
    return new JsonResult(new {id});
}
模型绑定器提供程序(验证模型绑定成功的条件)

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());
}

[更新]

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

控制器

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));
    }
}
[HttpGet]
public Task<JsonResult> Get([FromHeader] Guid id)
{
    return new JsonResult(new {id});
}
看看上面提到的Github讨论:

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

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异步任务UpdateAsync(Guid应用程序ID,[FromBody]updateApplicationRequest请求)
{}


简单明了,希望能有所帮助。

使用FromBody属性如何?我使用FromHeader属性是因为我想要的值在标题中而不是正文中。只是为了澄清,这似乎是一个特定于
[FromHeader]
绑定源的问题。我可以正确地从查询字符串和主体绑定guid。