2015-01-26 13 views
13

Stavo convertendo un progetto Webapi C# in F # utilizzando i modelli F # ASP.NET. Tutto funziona alla grande, tranne i parametri di query opzionali. Continuo a ricevere questo erroreUtilizzo dei parametri di query facoltativi nel progetto A # Web Api

{ 
    "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 # funzione di firma:

[<HttpGet; Route("")>] 
member x.GetVendorFiles([<Optional; DefaultParameterValue(100)>] count, [<Optional; DefaultParameterValue(null)>] start : Nullable<DateTime>) = 

C# funzione di firma:

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

Qualcuno sa di alcuna soluzione alternativa?

Aggiornamento: Ho scoperto la causa di questo problema. ASP.NET estrae i valori predefiniti per le azioni del controller using ParameterInfo. Apparentemente il compilatore F # non compila i valori di default allo stesso modo di C# (anche con il DefaultParameterValueAttribute)

Qual è il modo migliore o di lavorare intorno a questo? Potrebbe essere un filtro che devo iniettare o implementare il mio ParameterBinding?

+0

è possibile specificare i parametri predefiniti in una route specifica per il metodo controller? 'routes.MapRoute (nome:" Default ", url:" MyController/GetVendorFiles/{count}/{start} ", valori predefiniti: new {count = 100, start = (DateTime?) null});' – DLeh

+0

@DLeh Not perché l'applicazione front-end esistente utilizza il conteggio e inizia come parametri di query (non i parametri del percorso), quindi mi piacerebbe che fosse compatibile con il codice esistente. Nel peggiore dei casi, posso cambiarlo in quello – Pavel

+1

Ho appena notato che @Pavel ha sollevato questo problema come [problema Mvc] (https://github.com/aspnet/Mvc/issues/1923). Il problema contiene ulteriori discussioni e una soluzione alternativa. – bentayloruk

risposta

1

Sei praticamente limitato ai workaround, a quanto pare.

Cosa succede ad usare:

[<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 
+0

Questo è valido e l'ho provato, ma il problema è che se non si specifica un parametro di interrogazione iniziale, ottengo l'errore sopra, cioè trova il percorso ma poiché il riflesso causa quell'errore fallisce prima ancora di arrivare al mio gestore codice. – Pavel

2

È possibile aggirare questo con un costume ActionFilterAttribute implementazione. Il seguente codice supporta l'uso dei valori DefaultParameterValue quando manca un argomento azione e quando un argomento azione ha un valore di tipo System.Reflection.Missing.

Il codice è anche nel 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) 
2

Si potrebbe utilizzare HttpRequestMessage e analizzare il dizionario stringa di query.

CURA ...

Per utilizzare HttpRequestMessage, diciamo che si dispone di una pagina di stringa di query:

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

avrei probabilmente usare un metodo di supporto per analizzare la stringa di query fuori dalla HttpRequestMessage, qualcosa In questo modo:

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 -> "" 
+0

Puoi fare un esempio? – slfan

Problemi correlati