4

我正在移植一个原型应用程序,我使用 elm 和 python 烧瓶来使用 elm 和一个温文尔雅的后端。elm 应用程序正在调用 API 以从站点加载信息并执行其他操作。获取请求似乎没有问题,但是当我从 elm 进行 POST 时,我的行为变得很有趣 - 适用于烧瓶,但该请求似乎没有被 suave 接受。

抱歉,帖子太长了,详情如下:

榆树代码:

--POST IS WORKING TO Flask BUT NOT TO Suave
testPassword editMode token =
    let
        data = 
            Json.Encode.object
                [ ( "email", Json.Encode.string editMode.email )
                , ( "password",  Json.Encode.string editMode.newValue )
                ]
        body = 
            Json.Encode.object [ ("data", data) ]
        decodeVal value = 
            Json.Decode.map2 RestResponse
                (field "success" Json.Decode.bool)        
                (field "errorMessage" Json.Decode.string)        
        valDecoder =
            Json.Decode.value
                |> Json.Decode.andThen decodeVal
        postTo =
            String.concat [ apiUrl, token, "/api/password/test" ]                
    in
    Json.Decode.field "data" valDecoder
        |> Http.post (postTo) (jsonBody body)
        |> Http.send UpdateValue            

在 chrome 中调试我可以看到 python 烧瓶 OPTIONS 请求通过并且响应表明需要 POST

General
Request URL: http://localhost:5000/api/password/test
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: 127.0.0.1:5000
Referrer Policy: no-referrer-when-downgrade
Response Headers
Access-Control-Allow-Headers: *
Access-Control-Allow-Origin: *
Allow: OPTIONS, POST
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Wed, 30 May 2018 09:35:08 GMT
Server: Werkzeug/0.14.1 Python/3.5.2

但是,对于 Suave,OPTIONS 请求是不完整的或中断的:

General
Request URL: http://localhost:8080/api/password/test
Referrer Policy: no-referrer-when-downgrade
Request Headers
Provisional headers are shown
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Origin: http://localhost:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36

我的问题是我需要在 suave 程序方面做什么才能使其正常工作?我怀疑它要么是温文尔雅的配置,要么我需要编写一个 WebPart 来响应选项请求。温文尔雅代码如下:

let setCORSHeaders =
    Console.WriteLine("Enabling cross origin requests")
    addHeader  "Access-Control-Allow-Origin" "*" 
    >=> setHeader "Access-Control-Allow-Headers" "token" 
    >=> addHeader "Access-Control-Allow-Headers" "content-type" 
    >=> addHeader "Access-Control-Allow-Methods" "GET,OPTIONS,POST,PUT" 

let allowCors : WebPart =
    choose [
        GET >=>
            fun context ->
                context |> (
                    setCORSHeaders )
    ]

let app =
    ..
    statefulForSession
    >=> allowCors
    >=> choose
        [ GET >=> choose
            [ //.. 
            ]
          POST >=> choose
            [ //other posts working 
              path "/api/password/test" >=> context apiController.passwordTest 
            ] 
          OPTIONS >=> choose
            [ //tried this as well but don't think it's correct
              path "/api/password/test" >=> context apiController.passwordTest 
            ] ]

let suaveCfg =
  { defaultConfig with
      serverKey = Convert.FromBase64String encodedkey
      cookieSerialiser = new JsonNetCookieSerialiser()
    }

[<EntryPoint>]
let main argv =
    startWebServer suaveCfg app
    0

谢谢阅读

4

1 回答 1

4

我怀疑这是你的问题:

let allowCors : WebPart =
    choose [
        GET >=>
            fun context ->
                context |> (
                    setCORSHeaders )
    ]

这里没有OPTIONS案例,因此当 OPTIONS 请求通过您的应用程序时,choose组合器找不到任何与请求匹配的内容并返回None,这意味着永远不会调用处理链的其他部分。在您的应用程序中,allowCors位于处理链的部分之前OPTIONS

let app =
    ..
    statefulForSession
    >=> allowCors
    >=> choose
        [ GET >=> choose
            [ //.. 
            ]
          // Elided the POST part here
          OPTIONS >=> choose
            [ //tried this as well but don't think it's correct
              path "/api/password/test" >=> context apiController.passwordTest 
            ] ]

我认为,在您的 WebPart中放置一个OPTIONS部分,您的代码应该可以工作。allowCors

编辑:allowCors此外,可以改进这段代码:

            fun context ->
                context |> (
                    setCORSHeaders )

任何时候fun a -> a |> otherFunction,您都可以用 . 替换该表达式otherFunction。所以你allowCors写的函数看起来像这样:

let allowCors : WebPart =
    choose [
        GET >=>
            fun context ->
                context |> (
                    setCORSHeaders )
    ]

但它可能看起来像这样:

let allowCors : WebPart =
    choose [
        GET >=> setCORSHeaders
    ]

更容易阅读,你不觉得吗?

于 2018-05-30T17:59:38.843 回答