简体   繁体   English

F#Suave的路线/商店/类别/%s /品牌/%s怎么样?

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

I can't figure how setup the router for a path like: 我无法想象如何为路径设置路由器:

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

I have the web store demo and it work for simple URLs, but I don't see how make more flexible configurations. 我有网上商店演示,它适用于简单的URL,但我不知道如何使更灵活的配置。

This is what I have: 这就是我所拥有的:

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

And what about this? 那怎么样?

// 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
    ]

what I'd bet you are doing with explicitly typing a PrintfFormat<_,_,_,_> so you can use the same format string to build and consume the url path as I do. 我敢打赌,你明确键入一个PrintfFormat<_,_,_,_>这样你就可以像我一样使用相同的格式字符串来构建和使用url路径。

Query params don't seem to work in the url for pathScan here are a few things that do work in pathScan 查询参数似乎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

Notice for the getEventDownload I had to have 2 different paths, one for the client to generate a proper url, and one for the server. 注意getEventDownload我必须有2个不同的路径,一个用于客户端生成正确的URL,另一个用于服务器。 which sucks. 这太糟糕了。

Here's an example webPart that works unrelated to the above examples: 以下是一个与上述示例无关的webPart示例:

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

as far as query params I'd think you'd be better off letting the path match, and returning invalid request messages for missing query params or something similar. 至于查询参数,我认为你最好让路径匹配,并返回无效的查询参数或类似的无效请求消息。

Of course you could do branching inside the pathScan match handler. 当然你可以在pathScan匹配处理程序中进行分支。

An example of handling query params: 处理查询参数的示例:

    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

or a WebPart sample of composing with queryParams 或者使用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