Shinyでユーザーがすぐ使えるダッシュボードが作れるのは皆さんご存知の通りですが、不正な処理が起こった時のエラー表示がやや目障り、という問題があります。
UX全盛の世の中で、このようなユーザーアンフレンドリーなUIは許されませんので、RESTでデータをやり取りする実装をサンプルにエラーハンドリングを行ってみたいと思います。
Shinyでエラーハンドリングを行うには2種類方法があります。
validate/need
req
validate/needのほうがフローのコントロールやエラーメッセージなどより柔軟な対応が取れるのですが、やや実装が複雑になります。ここではより簡単な(コード全体をいじらなくてもよい)、req()について解説させていただこうと思います。req()は評価結果がFALSE/NULLなどだった場合に内部でstop()を呼び出すので、これ以降のコードが実行されなくなります。validate/needよりも簡単と記述したのは上記のようなreqの乱暴かつ効果的な機能によるところがあり、関数型主義の人には邪道に映るかもしれませんが、シーンを選べばとにかく便利なので個人的にはおススメです。
一方reqはすべての処理を止めるだけなので、ユーザーとしてはなぜエラーになったのかが分かりません。なので、ダイアログを使用してreqで処理を止める前に止める理由をユーザーに教えてあげるのが親切でしょう。以下サンプルで処理の流れを説明させていただきます。
※ちなみにreqはShiny version 0.13以上、ダイアログ(modalDialog)はversion 0.14以上が必要ですのでライブラリ更新してない方はアップデートしてください。
サンプルソースの全体像
こんな感じのフローを想定してみます。
(1)id/passをもとに、RESTでtokenを取得しにいく
(2)token取得後特定idのデータを取得する
(3)取得したデータを描画する
ui.R
id/passをテキストボックス・パスワードボックスで入力、session_loadというボタンを配置し、押すとdata_textにデータが返る構造です。
server.R抜粋
library(shiny)
library(httr)
p_client_id <- "123456789"
p_client_secret <- "qwertyuiop1234567890asdfghjkl"
ca_url <- "https://api.example.com/ep/login"
get_url <- "https://api.example.com/ep/events"
# session_loadボタンを押すとログイン→データを取得する関数
reload_data <- eventReactive(input$session_load, {
#ログイン認証
body <- list(grant_type = "password", username=input$id, password = input$pass, client_id = p_client_id, client_secret = p_client_secret)
#verbose()を入れるとこちらが渡した値と返ってきた値の詳細が見えます
r <- POST(ca_url, body = body, encode = "multipart", content_type = "application/x-www-form-urlencoded", verbose())
#トークンなどの返りをリストに格納
li <- content(r, "parsed")
#セッション情報を破棄
handle_reset(ca_url)
#データの取得
query <- list(type = "app_data", id = "1234567890")
r <- GET(ze_url, query = query, add_headers(Accept = ""), add_headers(Authorization = paste("Token",li$token),"Accept-Language" = "ja", Accept = "application/json", verbose()))
handle_reset(get_url)
el <- content(r, "parsed") #データ部分を格納します
return(list(li=li,el=el))
#本当はここにrevoke必要
}
output$data_text <- renderText({
el<- reload_data()[["el"]]
el$data
})
問題
さて、ここでid/passに不正値がセットされた場合、output$data_textはデータが取得できないため、例の赤のエラー画面になってしまいます。このコードのエラーハンドリングをするにはどうしたら良いでしょうか?
答え
トークンを取得した後(handle_reset(ca_url)の後)に以下のコードを挟みます。トークンを取得できなかった場合li$tokenがNULLになるので、まずエラーダイアログを表示し、その後req()を使用して後ろの処理を全て止めます。
if(is.null(li$access_token)){
showModal(modalDialog(title = "エラー", "検索条件に該当するデータがありません", easyClose = TRUE, footer = modalButton("OK")))
}
req(li$access_token) #ログイン失敗時はここで止めてしまう

以上。簡単ですね!
あまり多用しすぎるとスパゲッティになると思うので、ここぞという時にreq()を使ってみてください。