是否可以将以&符号分隔的无键 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
}
外部服务正在调用我的 .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
}