Подтвердить что ты не робот

Использование необязательных параметров запроса в проекте F # Web Api

Я преобразовал проект web-проекта С# в F #, используя шаблоны F # ASP.NET. Все работает отлично, за исключением необязательных параметров запроса. Я продолжаю получать эту ошибку

{
    "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> ) =

Функциональная подпись С#:

[HttpGet]
[Route("")]
public async Task<HttpResponseMessage> GetVendorFiles(int count = 100,DateTime? start = null)

Кто-нибудь знает об обходных решениях?

Обновлено: Я понял причину этой проблемы. ASP.NET извлекает значения по умолчанию для действий контроллера с помощью ParameterInfo. По-видимому, компилятор F # не компилирует значения по умолчанию так же, как С# (даже с DefaultParameterValueAttribute)

Какой лучший способ или обходится это? Будет ли это какой-то фильтр, который мне нужно внедрить или реализовать собственный ParameterBinding?

4b9b3361

Ответ 1

Вы можете обойти это с помощью пользовательской реализации ActionFilterAttribute. Следующий код поддерживает использование значений DefaultParameterValue, когда отсутствует аргумент действия, и когда аргумент действия имеет значение типа System.Reflection.Missing.

Код также находится в Gist ActionFilter для поддержки ASP.NET Web API DefaultParameterValue в 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)

Ответ 2

Вы можете использовать HttpRequestMessage и проанализировать словарь строки запроса.

EDITED...

Чтобы использовать HttpRequestMessage, скажем, у вас есть строка строки запроса:

[<Route("api/Applications")>]
[<HttpGet()>]
member x.Get (message:HttpRequestMessage) = 
    let page = GetQueryStringParameter(message, "page") 

Я бы, вероятно, использовал вспомогательный метод для синтаксического анализа строки запроса из HttpRequestMessage, примерно так:

let GetQueryStringParameter (message:HttpRequestMessage, value) = 
    let value = List.ofSeq ( message.GetQueryNameValuePairs() )
                |> List.tryFind (fun item -> item.Key = value)
    match value with
    | Some x -> x.Value
    | None   -> ""

Ответ 3

Похоже, вы почти ограничены обходными решениями.

Как насчет использования:   

[<HttpGet; Route("")>]
member x.GetVendorFiles(count: Nullable<int>, start : Nullable<DateTime>) =
    let count' =
        if obj.ReferenceEquals(count,null)
          then 100
          else count.Value

    // continue with action logic, using count' instead of count