LoginSignup
5
3

More than 5 years have passed since last update.

Shinyでreq()とmodalDialogを使ってエラーハンドリングを実装する

Last updated at Posted at 2016-11-08

Shinyでユーザーがすぐ使えるダッシュボードが作れるのは皆さんご存知の通りですが、不正な処理が起こった時のエラー表示がやや目障り、という問題があります。

真っ赤っか~
スクリーンショット 2016-11-08 16.10.15.png

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) #ログイン失敗時はここで止めてしまう

Screenshot_20161111-151600.png

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

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3