这是另一种可用于此类情况的解决方案:
解决方案1更加复杂,使用IActionConstrain和ModelBinders(这使您可以将输入绑定到特定的DTO):
您面临的问题是,您的控制器对于接收不同参数的2个不同方法具有相同的路由。 让我用一个类似的示例来说明,您可以像这样拥有2个方法:
Get(string entityName, long id)
Get(string entityname, string timestamp)
到目前为止,这是有效的,至少C#不会因为参数重载而给出错误。但是对于控制器来说,你有一个问题,当aspnet接收到额外的参数时,它不知道要将您的请求重定向到哪里。您可以更改路由,这是一种解决方案。
通常我更喜欢保持相同的名称,并将参数包装在DtoClass上,例如IntDto和StringDto。
public class IntDto
{
public int i { get; set; }
}
public class StringDto
{
public string i { get; set; }
}
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IActionResult Get(IntDto a)
{
return new JsonResult(a);
}
[HttpGet]
public IActionResult Get(StringDto i)
{
return new JsonResult(i);
}
}
但是,您仍然遇到了错误。为了将输入绑定到方法中的特定类型,我创建了一个ModelBinder。对于这种情况,它在下面(请注意,我正在尝试从查询字符串解析参数,但我正在使用一个区分符标头,该标头通常用于客户端和服务器之间的内容协商(内容协商):
public class MyModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
dynamic model = null;
string contentType = bindingContext.HttpContext.Request.Headers.FirstOrDefault(x => x.Key == HeaderNames.Accept).Value;
var val = bindingContext.HttpContext.Request.QueryString.Value.Trim('?').Split('=')[1];
if (contentType == "application/myContentType.json")
{
model = new StringDto{i = val};
}
else model = new IntDto{ i = int.Parse(val)};
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
接下来您需要创建一个ModelBinderProvider(如果我收到要绑定这些类型之一的请求,则使用MyModelBinder)
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(IntDto) || context.Metadata.ModelType == typeof(StringDto))
return new MyModelBinder();
return null;
}
把它注册到容器中。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new MyModelBinderProvider());
});
}
到目前为止,你还没有解决你的问题,但我们很接近了。现在为了命中控制器操作,你需要在请求中传递一个头部类型:
application/json或
application/myContentType.json。但是为了支持条件逻辑以确定是否选择关联的动作方法对于给定的请求是有效的还是无效的,你可以创建自己的
ActionConstraint
。基本上这里的想法是用这个属性装饰你的ActionMethod,限制用户只能在传递正确的媒体类型时才能访问该操作。请参阅下面的代码和如何使用它。
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class RequestHeaderMatchesMediaTypeAttribute : Attribute, IActionConstraint
{
private readonly string[] _mediaTypes;
private readonly string _requestHeaderToMatch;
public RequestHeaderMatchesMediaTypeAttribute(string requestHeaderToMatch,
string[] mediaTypes)
{
_requestHeaderToMatch = requestHeaderToMatch;
_mediaTypes = mediaTypes;
}
public RequestHeaderMatchesMediaTypeAttribute(string requestHeaderToMatch,
string[] mediaTypes, int order)
{
_requestHeaderToMatch = requestHeaderToMatch;
_mediaTypes = mediaTypes;
Order = order;
}
public int Order { get; set; }
public bool Accept(ActionConstraintContext context)
{
var requestHeaders = context.RouteContext.HttpContext.Request.Headers;
if (!requestHeaders.ContainsKey(_requestHeaderToMatch))
{
return false;
}
foreach (var mediaType in _mediaTypes)
{
var mediaTypeMatches = string.Equals(requestHeaders[_requestHeaderToMatch].ToString(),
mediaType, StringComparison.OrdinalIgnoreCase);
if (mediaTypeMatches)
{
return true;
}
}
return false;
}
}
这是您的最终更改:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
[RequestHeaderMatchesMediaTypeAttribute("Accept", new[] { "application/json" })]
public IActionResult Get(IntDto a)
{
return new JsonResult(a);
}
[RequestHeaderMatchesMediaTypeAttribute("Accept", new[] { "application/myContentType.json" })]
[HttpGet]
public IActionResult Get(StringDto i)
{
return new JsonResult(i);
}
}
现在如果您运行应用程序,错误已经消失。但是,如何传递参数呢?:
这个将会调用这个方法:
public IActionResult Get(StringDto i)
{
return new JsonResult(i);
}
![application/myContentType.json](https://istack.dev59.com/rE6nY.webp)
这个是关于编程的,另一个是:
(未提供)
public IActionResult Get(IntDto a)
{
return new JsonResult(a);
}
![application/json](https://istack.dev59.com/nkXBC.webp)
解决方案2:路由限制。
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet("{i:int}")]
public IActionResult Get(int i)
{
return new JsonResult(i);
}
[HttpGet("{i}")]
public IActionResult Get(string i)
{
return new JsonResult(i);
}
}
这是一种测试,因为我正在使用默认路由:
https://localhost:44374/weatherforecast/"test" should go to the one that receives the string parameter
https://localhost:44374/weatherforecast/1
应该转到接收整数参数的那个。