我正在使用F#ASP.NET模板将C#webapi项目转换为F#.除了可选的查询参数,一切都很好.我不断得到这个错误
{ "message": "The request is invalid.","messageDetail": "The parameters dictionary contains an invalid entry for parameter 'start' for method 'System.Threading.Tasks.Task`1[System.Net.Http.HttpResponseMessage] GetVendorFiles(Int32,System.Nullable`1[System.DateTime])' in 'Thor.WebApi.VendorFilesController'. The dictionary contains a value of type 'System.Reflection.Missing',but the parameter requires a value of type 'System.Nullable`1[System.DateTime]'." }
F#函数签名:
[<HttpGet; Route("")>] member x.GetVendorFiles( [<Optional; DefaultParameterValue(100)>] count,[<Optional; DefaultParameterValue(null)>] start : Nullable<DateTime> ) =
C#函数签名:
[HttpGet] [Route("")] public async Task<HttpResponseMessage> GetVendorFiles(int count = 100,DateTime? start = null)
有没有人知道任何解决方法?
更新:
我弄清了这个问题的原因. ASP.NET提取控制器操作using ParameterInfo的默认值.显然,F#编译器不会像C#一样编译默认值(即使使用DefaultParameterValueAttribute)
什么是最好的方式或解决这个问题?是否需要注入或实现自己的ParameterBinding的一些过滤器?
解决方法
您可以使用自定义ActionFilterAttribute实现解决此问题.以下代码支持在缺少操作参数时使用DefaultParameterValue值,以及当action参数具有System.Reflection.Missing类型的值时.
代码也在Gist ActionFilter for ASP.NET Web API DefaultParameterValue support in F#中.
namespace System.Web.Http open System.Reflection open System.Web.Http.Filters open System.Web.Http.Controllers open System.Runtime.InteropServices /// Responsible for populating missing action arguments from DefaultParameterValueAttribute values. /// Created to handle this issue https://github.com/aspnet/Mvc/issues/1923 /// Note: This is for later version of System.Web.Http but could be back-ported. type DefaultParameterValueFixupFilter() = inherit ActionFilterAttribute() /// Get list of (paramInfo,defValue) tuples for params where DefaultParameterValueAttribute is present. let getDefParamVals (parameters:ParameterInfo array) = [ for param in parameters do let defParamValAttrs = param.GetCustomAttributes<DefaultParameterValueAttribute>() |> List.ofSeq match defParamValAttrs with // Review: we are ignoring null defaults. Is this correct? | [x] -> if x.Value = null then () else yield param,x.Value | [] -> () | _ -> failwith "Multiple DefaultParameterValueAttribute on param '%s'!" param.Name ] /// Add action arg default values where specified in DefaultParameterValueAttribute attrs. let addActionArgDefsFromDefParamValAttrs (context:HttpActionContext) = match context.ActionDescriptor with | :? ReflectedHttpActionDescriptor as ad -> let defParamVals = getDefParamVals (ad.MethodInfo.GetParameters()) for (param,value) in defParamVals do match context.ActionArguments.TryGetValue(param.Name) with | true,:? System.Reflection.Missing | false,_ -> // Remove is null-op if key not found,so we handle both match cases OK. let _ = context.ActionArguments.Remove(param.Name) context.ActionArguments.Add(param.Name,value) | _,_ -> () | _ -> () /// Override adding suport for DefaultParameterValueAttribute values. override x.OnActionExecuting(context) = addActionArgDefsFromDefParamValAttrs context base.OnActionExecuting(context) /// Override adding suport for DefaultParameterValueAttribute values. override x.OnActionExecutingAsync(context,cancellationToken) = addActionArgDefsFromDefParamValAttrs context base.OnActionExecutingAsync(context,cancellationToken)