mirror of
https://github.com/rstudio/shiny.git
synced 2026-04-29 03:00:45 -04:00
Changed customStop to stop(safeError). Refactored some middleware.R code. Fixed downloadHandler's bug of not responding to safeError.
This commit is contained in:
@@ -131,8 +131,7 @@ withLogErrors <- function(expr,
|
||||
captureStackTraces(expr),
|
||||
error = function(cond) {
|
||||
# Don't print shiny.silent.error (i.e. validation errors)
|
||||
if (inherits(cond, "shiny.silent.error"))
|
||||
return()
|
||||
if (inherits(cond, "shiny.silent.error")) return()
|
||||
if (isTRUE(getOption("show.error.messages"))) {
|
||||
printError(cond, full = full, offset = offset)
|
||||
}
|
||||
|
||||
@@ -307,29 +307,19 @@ HandlerManager <- R6Class("HandlerManager",
|
||||
}
|
||||
},
|
||||
call = .httpServer(
|
||||
function(req,
|
||||
full = getOption("shiny.fullstacktrace", FALSE),
|
||||
offset = getOption("shiny.stacktraceoffset", TRUE)) {
|
||||
|
||||
withCallingHandlers(captureStackTraces(handlers$invoke(req)),
|
||||
error = function(cond) {
|
||||
# Don't print shiny.silent.error (i.e. validation errors)
|
||||
if (inherits(cond, "shiny.silent.error")) return()
|
||||
if (isTRUE(getOption("show.error.messages"))) {
|
||||
printError(cond, full = full, offset = offset)
|
||||
sanitizeErrors = getOption('shiny.sanitize.errors', TRUE)
|
||||
|
||||
if (inherits(cond, 'shiny.custom.error') || !sanitizeErrors) {
|
||||
stop(cond$message, call. = FALSE)
|
||||
}
|
||||
else {
|
||||
stop(paste("An error has occurred. Check your logs or",
|
||||
"contact the app author for clarification."),
|
||||
call. = FALSE)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
function (req) {
|
||||
withCallingHandlers(withLogErrors(handlers$invoke(req)),
|
||||
error = function(cond) {
|
||||
sanitizeErrors <- getOption('shiny.sanitize.errors', TRUE)
|
||||
if (inherits(cond, 'shiny.custom.error') || !sanitizeErrors) {
|
||||
stop(cond$message, call. = FALSE)
|
||||
} else {
|
||||
stop(paste("An error has occurred. Check your logs or",
|
||||
"contact the app author for clarification."),
|
||||
call. = FALSE)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
getOption('shiny.sharedSecret')
|
||||
),
|
||||
|
||||
10
R/shiny.R
10
R/shiny.R
@@ -97,11 +97,11 @@ NULL
|
||||
#' of a function appears next to the srcref where it is defined, rather than
|
||||
#' where it is currently being called from.}
|
||||
#' \item{shiny.sanitize.errors}{If \code{TRUE} (the default), then normal
|
||||
#' errors (i.e errors generated by \code{stop}) won't show up in the app; a
|
||||
#' simple generic error message is printed instead (the error and strack trace
|
||||
#' errors (i.e. errors not wrapped in \code{safeError}) won't show up in the app;
|
||||
#' a simple generic error message is printed instead (the error and strack trace
|
||||
#' printed to the console remain unchanged). If you want this behavior in
|
||||
#' general, but you DO want a particular error message to get displayed to the
|
||||
#' user, please use \code{customStop} instead.}
|
||||
#' general, but you DO want a particular error \code{e} to get displayed to the
|
||||
#' user, please use \code{stop(safeError(e))} instead.}
|
||||
#' }
|
||||
#' @name shiny-options
|
||||
NULL
|
||||
@@ -889,7 +889,7 @@ ShinySession <- R6Class(
|
||||
)), silent = TRUE)
|
||||
if (inherits(result, 'try-error')) {
|
||||
unlink(tmpdata)
|
||||
stop(result)
|
||||
stop(attr(result, "condition", exact = TRUE))
|
||||
}
|
||||
return(httpResponse(
|
||||
200,
|
||||
|
||||
46
R/utils.R
46
R/utils.R
@@ -878,27 +878,29 @@ columnToRowData <- function(data) {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Create a custom error
|
||||
#' Declare an error safe for the user to see
|
||||
#'
|
||||
#' This should be used when you want to create a custom error to which error
|
||||
#' default options do not apply. In particular, \code{customStop} will
|
||||
#' This should be used when you want to let the user see an error
|
||||
#' message even if the default is to sanitize all errors. If you have an
|
||||
#' error \code{e} and call \code{stop(safeError(e))}, then Shiny will
|
||||
#' ignore the value of \code{getOption("shiny.sanitize.errors")} and always
|
||||
#' display the error in the app itself.
|
||||
#'
|
||||
#' @param error Either an "error" object or a "character" object (string).
|
||||
#' The former will just be rethrown, while the latter will be used as the
|
||||
#' message of the error returned by \code{customStop}.
|
||||
#' In the latter case, the string will become the message of the error
|
||||
#' returned by \code{safeError}.
|
||||
#' @param errorClass An optional class to add to the error.
|
||||
#'
|
||||
#' @details An error generated by \code{customStop} has priority over all
|
||||
#' @return An "error" object
|
||||
#'
|
||||
#' @details An error generated by \code{safeError} has priority over all
|
||||
#' other Shiny errors. This can be dangerous. For example, if you have set
|
||||
#' \code{options(shiny.sanitize.errors = TRUE)}, then by default all error
|
||||
#' messages are omitted in the app, and replaced by a generic error message.
|
||||
#' However, this does not apply to \code{customStop}: whatever you pass
|
||||
#' However, this does not apply to \code{safeError}: whatever you pass
|
||||
#' through \code{error} will be displayed to the user. So, this should only
|
||||
#' be used when you are sure that your error message does not contain any
|
||||
#' sensitive information. In those situations, \code{customStop} can make
|
||||
#' sensitive information. In those situations, \code{safeError} can make
|
||||
#' your users' lives much easier by giving them a hint as to where the
|
||||
#' error occurred.
|
||||
#'
|
||||
@@ -915,13 +917,13 @@ columnToRowData <- function(data) {
|
||||
#' # Define UI
|
||||
#' ui <- fluidPage(
|
||||
#' textInput('number', 'Enter your favorite number from 1 to 10', '5'),
|
||||
#' textOutput('errorStop'),
|
||||
#' textOutput('errorCustomStop')
|
||||
#' textOutput('normalError'),
|
||||
#' textOutput('safeError')
|
||||
#' )
|
||||
#'
|
||||
#' # Server logic
|
||||
#' server <- function(input, output) {
|
||||
#' output$errorStop <- renderText({
|
||||
#' output$normalError <- renderText({
|
||||
#' number <- input$number
|
||||
#' if (number %in% 1:10) {
|
||||
#' return(paste('You chose', number, '!'))
|
||||
@@ -931,14 +933,14 @@ columnToRowData <- function(data) {
|
||||
#' )
|
||||
#' }
|
||||
#' })
|
||||
#' output$errorCustomStop <- renderText({
|
||||
#' output$safeError <- renderText({
|
||||
#' number <- input$number
|
||||
#' if (number %in% 1:10) {
|
||||
#' return(paste('You chose', number, '!'))
|
||||
#' } else {
|
||||
#' customStop(
|
||||
#' stop(safeError(
|
||||
#' paste(number, 'is not a number between 1 and 10')
|
||||
#' )
|
||||
#' ))
|
||||
#' }
|
||||
#' })
|
||||
#' }
|
||||
@@ -947,15 +949,21 @@ columnToRowData <- function(data) {
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
customStop <- function(error, errorClass = character(0)) {
|
||||
safeError <- function(error, errorClass = character(0)) {
|
||||
if (inherits(error, "error")) {
|
||||
class(error) <- c("shiny.custom.error", errorClass, class(error))
|
||||
stop(error)
|
||||
cond <- structure(
|
||||
list(message = error$message),
|
||||
class = c("shiny.custom.error", errorClass, class(error))
|
||||
)
|
||||
} else if (inherits(error, "character")) {
|
||||
stopWithCondition(c("shiny.custom.error", errorClass), error)
|
||||
cond <- structure(
|
||||
list(message = error),
|
||||
class = c("shiny.custom.error", errorClass, "error", "condition")
|
||||
)
|
||||
} else {
|
||||
stop("The class of the `error` parameter must be either 'error' or 'character'")
|
||||
}
|
||||
return(cond)
|
||||
}
|
||||
|
||||
#' Validate input values and other conditions
|
||||
|
||||
Reference in New Issue
Block a user