簡體   English   中英

F#Suave的路線/商店/類別/%s /品牌/%s怎么樣?

[英]How route /store/category/%s/brand/%s in F# Suave?

我無法想象如何為路徑設置路由器:

/store/category/%s/brand/%s

我有網上商店演示,它適用於簡單的URL,但我不知道如何使更靈活的配置。

這就是我所擁有的:

type StrPath = PrintfFormat<(string -> string),unit,string,string,string>
// How do this?
type Str2Path = PrintfFormat<(string -> string),unit,string,string,string>

let withParam (key,value) path = sprintf "%s?%s=%s" path key value

module Store =
    //Don't know what put here
    let browseBrand = sprintf "/store/category/%s/brand/%s"
    //This work ok
    let browseCategory : StrPath = "/store/category/%s"
// I need to capture query parameters
let browseBrand cat brand = request (fun r ->
    Views.browse(cat brand))

let webPart = 
    localizeUICulture >>
    choose [
        path Path.Store.overview >=> overview
        pathScan Path.Store.browseBrand browseBrand
        pathScan Path.Store.browseCategory browseCategory

那怎么樣?

// note the string tuple as return value
type Str2Path = PrintfFormat<(string -> string -> string),unit,string,string,(string * string)>

module Store =
    // your path
    let browseBrand : Str2Path = "/store/category/%s/brand/%s"

// again, note the tuple as input
let browseBrand (cat, brand) = request (Views.browse(cat brand))

let webPart = 
    localizeUICulture >>
    choose [
        pathScan Store.browseBrand browseBrand
        // ... OMMITED
    ]

我敢打賭,你明確鍵入一個PrintfFormat<_,_,_,_>這樣你就可以像我一樣使用相同的格式字符串來構建和使用url路徑。

查詢參數似乎pathScan用於pathScan的url這里有一些在pathScan有效的pathScan

let clientEvent clientId = sprintf "/client/%i/event" clientId
let summary eventId = sprintf "/event/%i/summary" eventId
// you can use units of measure in your format strings
let getEventValidation () : PrintfFormat<int<EventId> -> _,_,_,_,int<EventId>> = "/event/%i/validation"
let checkoutUploaded () : PrintfFormat<int<CheckoutId> -> _ -> _ ,_,_,_,_> = "/checkout/%i/uploaded/username/%s"

let getEventDownloadNoQuery () : PrintfFormat<int<EventId> -> _,_,_,_,_> = "/event/%i/download"
let userName="userName"
let tabletIdent = "tabletIdent"
let getEventDownload () : PrintfFormat<int<EventId> -> _ -> _ -> _,_,_,_,_> = "/event/%i/download?userName=%s&tabletIdent=%s"

// we can use the actual format string as the method/variable name
// is it a good idea? not sure.
// get participant for edit
let ``get /participant/%i``() : PrintfFormat<int<ParticipantId> -> _,_,_,_,int<ParticipantId>> = "/participant/%i"
let getUsers = "/user"

// we can include the action in the variable name too
// also questionable but possibly useful
let ``post /participant`` = "/participant"


let ``get /client/%i/participant`` () : PrintfFormat<int<ClientId> -> _,_,_,_,int<ClientId>> = "/client/%i/participant"
let ``get /event/%i/participants`` () : PrintfFormat<int<EventId> -> _,_,_,_,int<EventId>> = "/event/%i/participants"
let resultList clientId pId = sprintf "/result/client/%i/participant/%i" clientId pId

注意getEventDownload我必須有2個不同的路徑,一個用於客戶端生成正確的URL,另一個用於服務器。 這太糟糕了。

以下是一個與上述示例無關的webPart示例:

pathScan "/client/%i/customer/%i" (fun (clientId,customerId) -> sprintf "Customer %i, Customer %i" clientId customerId |> OK)

至於查詢參數,我認為你最好讓路徑匹配,並返回無效的查詢參數或類似的無效請求消息。

當然你可以在pathScan匹配處理程序中進行分支。

處理查詢參數的示例:

    let serveResult cn :WebPart =
        fun ctx ->
            let eventIdField = toCamelCase RMeta.EventId
            let pIdField  = toCamelCase RMeta.ParticipantId
            let eventIdOpt = ctx.request.queryParamOpt eventIdField
            let pIdOpt = ctx.request.queryParamOpt pIdField
            match eventIdOpt, pIdOpt with
            | Some(_,Some (ParseInt eventId)), Some(_,Some(ParseInt pId)) ->
                let model = Dal.DataAccess.Results.getResult cn (1<EventId> * eventId) (1<ParticipantId> * pId)
                match model with
                | Some m ->
                    OK (Json.toJson m) // |> Option.getOrDefault' (lazy({ResultRecord.Zero() with EventId = eventId; ParticipantId = pId}))
                | _ -> RequestErrors.NOT_FOUND ctx.request.rawQuery

            | _ ->  RequestErrors.BAD_REQUEST (ctx.request.rawQuery)
            |> fun f -> f ctx

或者使用queryParams編寫的WebPart示例

let routing () = 
    let (|ParseInt|_|) =
        function
        | null | "" -> None
        | x -> 
            match Int32.TryParse x with
            | true, i -> Some i
            | _ -> None


    // this only returns a string but hopefully it helps imply how more complicated items could be composed
    let queryParamOrFail name (ctx:HttpContext) =
        match ctx.request.queryParam name with
        | Choice1Of2 value -> 
            Choice1Of2 value
        | Choice2Of2 msg ->
            RequestErrors.BAD_REQUEST msg
            |> Choice2Of2
    let queryIntOrFail name =
        queryParamOrFail name
        >> Choice.bind(
            (|ParseInt|_|)
            >> function
                | Some i -> Choice1Of2 i
                | None -> RequestErrors.BAD_REQUEST (sprintf "query param %s was not a number" name) |> Choice2Of2
        )
    let clientQueryPart:WebPart =
        path "/clientQuery" >=>
        (fun ctx ->
            queryIntOrFail "companyId" ctx
            |> function
                | Choice1Of2 v -> sprintf "CompanyId %i" v |> OK
                | Choice2Of2 requestErrorWebPart -> requestErrorWebPart 
            |> fun wp -> wp ctx
        )
    let fullQueryPart:WebPart =
        path "/query" >=>
        (fun ctx ->
            match queryIntOrFail "companyId" ctx, queryIntOrFail "clientId" ctx, queryIntOrFail "customerId" ctx with
            | Choice2Of2 reqErr,_,_ -> reqErr
            | _,Choice2Of2 reqErr,_ -> reqErr
            | _,_,Choice2Of2 reqErr -> reqErr
            | Choice1Of2 compId, Choice1Of2 clientId, Choice1Of2 customerId ->
                sprintf "CompanyId %i, ClientId %i, CustomerId %i" compId clientId customerId
                |> OK
            |> fun wp -> wp ctx
        )

    choose 
        [
            GET >=> choose
                [   
                    path "/" >=> OK "Default GET"
                    path "/hello" >=> OK "Hello GET"
                    pathScan "/whatnumber/%i" ((sprintf "Your number is %i") >> OK)
                    pathScan "/client/%i/customer/%i" (fun (clientId,customerId) -> sprintf "Client %i, Customer %i" clientId customerId |> OK)
                    pathScan "/client/%i/customerQuery" (fun clientId ctx -> 
                        match queryParamOrFail "customerId" ctx with
                        | Choice1Of2 (ParseInt customerId) ->
                            sprintf "Client %i, Customer %i" clientId customerId
                            |> fun msg ->  OK msg ctx
                        | Choice1Of2 _ -> RequestErrors.BAD_REQUEST "query param customerId was not a number" ctx
                        | Choice2Of2 wp -> wp ctx
                    )
                    clientQueryPart
                    fullQueryPart
                    path "/goodbye" >=> OK "Good bye GET"
                ]
            POST >=> choose
                [
                    path "/hello" >=> OK "Hello POST"
                    path "/goodbye" >=> OK "Good bye POST"
                ]
        ]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM