使用 Elm 的 Trefle API 请求

问题描述 投票:0回答:1

我从 Elm 开始,希望在搜索植物时从 Trefle API 获取图像,但我不断收到“无法获取图像”错误。 我确实使用自己的访问令牌,但我在此处将其替换为“MY_API_TOKEN”以保持其私密性。 你能帮我理解我的代码中的问题吗?

我尝试获取更详细的错误消息,但后来我的代码崩溃了,因为我对使用 elm 和 Web 开发领域还很陌生。

module Main exposing (..)

import Browser
import Html exposing (Html, button, div, img, input, text)
import Html.Attributes exposing (placeholder, src, type_)
import Html.Events exposing (onClick, onInput)
import Http
import Json.Decode exposing (Decoder, field, list, string)
import Maybe exposing (withDefault, map)


type alias Model =
    { searchTerm : String
    , imageUrl : String
    , error : String
    }


init : () -> ( Model, Cmd Msg )
init _ =
    ( { searchTerm = "", imageUrl = "", error = "" }, Cmd.none )


type Msg
    = UpdateSearchTerm String
    | FetchImage
    | ReceiveImage (Result Http.Error String)


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        UpdateSearchTerm term ->
            ( { model | searchTerm = term }, Cmd.none )

        FetchImage ->
            ( model, fetchPlantImage model.searchTerm )

        ReceiveImage result ->
            case result of
                Ok url ->
                    ( { model | imageUrl = url, error = "" }, Cmd.none )

                Err _ ->
                    ( { model | error = "Failed to fetch image." }, Cmd.none )


view : Model -> Html Msg
view model =
    div []
        [ input [ placeholder "Enter plant name", onInput UpdateSearchTerm ] []
        , button [ onClick FetchImage ] [ text "Search" ]
        , if model.imageUrl /= "" then
            img [ src model.imageUrl ] []
          else
            text ""
        , if model.error /= "" then
            div [] [ text model.error ]
          else
            text ""
        ]


fetchPlantImage : String -> Cmd Msg
fetchPlantImage searchTerm =
    let
        url =
            "https://trefle.io/api/v1/plants/search?token=MY_API_TOKEN&q=" ++ searchTerm

        decodeImageUrl : Decoder String
        decodeImageUrl =
            field "data" (list (field "image_url" string))
                |> Json.Decode.map (withDefault "" << map identity << List.head)
    in
    Http.get
        { url = url
        , expect = Http.expectJson ReceiveImage decodeImageUrl
        }


main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = \_ -> Sub.none
        , view = view
        }
javascript json http error-handling elm
1个回答
0
投票

tl;dr,trefle.io 的 SSL 证书似乎已过期,因此这可能是他们的问题。

我做的第一件事是更新您的 HTTP 响应处理程序,使其在报告错误时更加明确(而不仅仅是“无法获取图像”。)

module Main exposing (..)

import Browser
import Html exposing (Html, button, div, img, input, text)
import Html.Attributes exposing (placeholder, src, type_)
import Html.Events exposing (onClick, onInput)
import Http
import Json.Decode exposing (Decoder, field, list, string)
import Maybe exposing (map, withDefault)


type alias Model =
    { searchTerm : String
    , imageUrl : String
    , error : String
    }


init : () -> ( Model, Cmd Msg )
init _ =
    ( { searchTerm = "", imageUrl = "", error = "" }, Cmd.none )


type Msg
    = UpdateSearchTerm String
    | FetchImage
    | ReceiveImage (Result Http.Error String)


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        UpdateSearchTerm term ->
            ( { model | searchTerm = term }, Cmd.none )

        FetchImage ->
            ( model, fetchPlantImage model.searchTerm )

        ReceiveImage result ->
            case result of
                Ok url ->
                    ( { model | imageUrl = url, error = "" }, Cmd.none )

                Err err ->
                    ( { model
                        | error =
                            case err of
                                Http.BadUrl errorMessage ->
                                    errorMessage

                                Http.Timeout ->
                                    "Timeout"

                                Http.NetworkError ->
                                    "Network error"

                                Http.BadStatus status ->
                                    "Bad status " ++ String.fromInt status

                                Http.BadBody errorMessage ->
                                    errorMessage
                      }
                    , Cmd.none
                    )


view : Model -> Html Msg
view model =
    div []
        [ input [ placeholder "Enter plant name", onInput UpdateSearchTerm ] []
        , button [ onClick FetchImage ] [ text "Search" ]
        , if model.imageUrl /= "" then
            img [ src model.imageUrl ] []

          else
            text ""
        , if model.error /= "" then
            div [] [ text model.error ]

          else
            text ""
        ]


fetchPlantImage : String -> Cmd Msg
fetchPlantImage searchTerm =
    let
        url =
            "https://trefle.io/api/v1/plants/search?token=MY_API_TOKEN&q=" ++ searchTerm

        decodeImageUrl : Decoder String
        decodeImageUrl =
            field "data" (list (field "image_url" string))
                |> Json.Decode.map (withDefault "" << map identity << List.head)
    in
    Http.get
        { url = url
        , expect = Http.expectJson ReceiveImage decodeImageUrl
        }


main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = \_ -> Sub.none
        , view = view
        }

艾莉:https://ellie-app.com/rqZy7NHmGrGa1

一旦执行此操作,您将看到实际错误是:

网络错误

表示服务无法访问。

接下来我只是尝试使用

curl
访问该 URL,这会报告服务器的 SSL 证书错误:

% curl "https://trefle.io/api/v1/plants/search?token=MY_API_TOKEN&q=coconut"
curl: (60) SSL certificate problem: certificate has expired
More details here: https://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.
HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.
© www.soinside.com 2019 - 2024. All rights reserved.