5

我不知道如何为如下路径设置路由器:

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

2 回答 2

1

那这个呢?

// 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
    ]
于 2017-04-25T12:22:14.667 回答
0

我敢打赌你正在做的是明确输入 aPrintfFormat<_,_,_,_>这样你就可以像我一样使用相同的格式字符串来构建和使用 url 路径。

查询参数似乎在 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

WebPart使用 queryParams 组成的示例

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"
                ]
        ]
于 2019-02-19T14:27:53.540 回答