我敢打赌你正在做的是明确输入 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"
]
]