[英]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.