我从 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
}
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.