是否可以将以&符号分隔的无键 url 参数提取到 .NET Core 中的操作方法参数中?

Is it possible to extract ampersand-separated keyless url parameters into action method parameters in .NET Core?

外部服务正在调用我的 .NET Core API,格式为 mysite.URL。com/date/2021&4&12。我无法控制它如何调用我的 API,所以我必须适应 URL 格式。

我可以在我的控制器操作方法中使用

解决这个问题
[HttpGet("date/{dateString}")]
public Thing GetThingByDate(string dateString)
{
  //...string manipulation on dateString
}

但是是否有可能让 .NET Core 完成工作,甚至自己编写一些东西,这样我就可以得到

[HttpGet("date/((some kind of formatting here))")]
public Thing GetThingByDate(int year, int month, int day)
{
}

是的,这是可能的。使它可重用的一种方法是为此创建自定义模型和模型绑定器。

首先是我天真的控制器实现:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{

    [HttpGet("{**slug}")]
    public IActionResult Get(MyDateModel slug)
    {
        return Ok(slug);
    }

}

[ModelBinder(BinderType = typeof(MyDateModelBinder))]
public class MyDateModel
{
    public int Year { get; set; }
    public int Month { get; set; }
    public int Day { get; set; }
}

{**slug} 是一个包罗万象的属性,因此所有尚未绑定的内容都将被放入一个名为 'slug' 的变量中。在我们的例子中是“2021&4&12”

我们还使用我们的年月日属性创建了一个新的视图模型,并将其绑定到我们尚未创建的模型绑定器。

现在所有的魔法都在这里发生。 Microsoft 在 https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-5.0 提供了一个很好的例子作为起点。我们的工作将以此为基础:

public class MyDateModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var modelName = bindingContext.ModelName;

        // Try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var rawValue = valueProviderResult.FirstValue;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(rawValue))
        {
            return Task.CompletedTask;
        }

        // parse the string to the target value
        try
        {
            var values = rawValue.Split("&");
            var model = new MyDateModel
            {
                Year = Int32.Parse(values[0]),
                Month = Int32.Parse(values[1]),
                Day = Int32.Parse(values[2]),
            };

            bindingContext.Result = ModelBindingResult.Success(model);
        }
        catch (Exception ex)
        {
            bindingContext.ModelState.TryAddModelError(
                modelName, ex.Message);

        }
        return Task.CompletedTask;
    }
}

我不打算讲太多细节,具体是哪一行发生了什么,因为你可以设置一个断点并获得一个好的尤里卡! - 片刻你的自我。请注意,这个相当幼稚的实现缺少很多稳健性和错误处理 - 因此,如果您的字符串格式错误或分隔符错误,它将无法工作,并且您的错误消息可能不适合客户。

在我们现在的示例中,我发出了请求 https://localhost:5001/api/values/2021&4&12 并且它返回了以下值 JSON:

{
    "year": 2021,
    "month": 4,
    "day": 12
}