[java] 스프링 부트 REST 서비스 예외 처리

대규모 REST 서비스 서버를 설정하려고합니다. 우리는 Spring Boot 1.2.1 Spring 4.1.5 및 Java 8을 사용하고 있습니다. 컨트롤러는 @RestController와 표준 @RequestMapping 주석을 구현하고 있습니다.

내 문제는 스프링 부트가 컨트롤러 예외에 대한 기본 리디렉션을로 설정한다는 것 /error입니다. 문서에서 :

Spring Boot는 기본적으로 / error 매핑을 제공하여 모든 오류를 합리적인 방식으로 처리하며 서블릿 컨테이너에 ‘전역’오류 페이지로 등록됩니다.

Node.js로 REST 애플리케이션을 작성하는 데 수년이 지난 지금, 이것은 나에게 합리적인 것입니다. 서비스 엔드 포인트가 생성하는 모든 예외는 응답으로 리턴되어야합니다. 응답을 찾고 있고 리디렉션에 대한 조치를 취할 수 없거나 수행하지 않는 Angular 또는 JQuery SPA 소비자에게 리디렉션을 보내는 이유를 이해할 수 없습니다.

내가하고 싶은 일은 의도적으로 요청 매핑 메소드에서 throw되거나 Spring에 의해 자동 생성 된 예외 (요청 경로 서명에 핸들러 메소드가없는 경우 404)를 취할 수있는 전역 오류 핸들러를 설정하는 것입니다. MVC 리디렉션없이 클라이언트에 표준 형식의 오류 응답 (400, 500, 503, 404). 특히, 오류를 가져 와서 UUID를 사용하여 NoSQL에 기록한 다음 JSON 본문에서 로그 항목의 UUID를 사용하여 올바른 HTTP 오류 코드를 클라이언트로 리턴합니다.

문서는이 작업을 수행하는 방법에 대해 모호했습니다. 나 자신에게 ErrorController 구현을 만들 거나 ControllerAdvice 를 어떤 방식으로 사용해야 하는 것처럼 보이지만 , 내가 본 모든 예제에는 여전히 일종의 오류 매핑으로 응답을 전달하는 것이 포함되어 있지만 도움이되지 않습니다. 다른 예제에서는 “Throwable”을 나열하고 모든 것을 가져 오는 대신 처리하려는 모든 예외 유형을 나열해야한다고 제안합니다.

누구든지 내가 놓친 것을 말하거나 Node.js가 다루기가 더 쉽다는 체인을 제안하지 않고이를 수행하는 방법에 대해 올바른 방향으로 나를 가리킬 수 있습니까?



답변

새로운 답변 (2016-04-20)

스프링 부트 1.3.1.RELEASE 사용하기

새로운 1 단계 -application.properties에 다음 특성을 추가하는 것이 쉽고 방해가되지 않습니다.

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

기존 DispatcherServlet 인스턴스를 수정하는 것보다 훨씬 쉽습니다 (아래 참조)! -JO ‘

전체 RESTful 애플리케이션으로 작업하는 경우 정적 자원을 처리하기 위해 Spring Boot의 기본 구성을 사용하는 경우 자원 핸들러가 요청을 처리하기 때문에 정적 자원의 자동 맵핑을 사용하지 않는 것이 매우 중요합니다. ** 즉, 응용 프로그램의 다른 처리기가 처리하지 않은 요청을 선택하므로 디스패처 서블릿은 예외를 던질 기회가 없습니다.


새로운 답변 (2015-12-04)

스프링 부트 1.2.7.RELEASE 사용하기

새로운 1 단계 – “throExceptionIfNoHandlerFound”플래그를 설정하는 훨씬 덜 관입적인 방법을 찾았습니다. 아래의 DispatcherServlet 대체 코드 (1 단계)를 애플리케이션 초기화 클래스에서이 코드로 바꾸십시오.

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

이 경우 기존 DispatcherServlet에 플래그를 설정하여 Spring Boot 프레임 워크에 의한 자동 구성을 유지합니다.

@EnableWebMvc 주석은 Spring Boot에 치명적입니다. 예,이 주석은 아래 설명 된대로 모든 컨트롤러 예외를 포착 할 수있는 것과 같은 기능을 가능하게하지만 Spring Boot가 일반적으로 제공하는 유용한 자동 구성을 많이 제거합니다. 스프링 부트를 사용할 때는 매우주의해서 주석을 사용하십시오.


원래 답변 :

여기에 게시 된 솔루션에 대한 더 많은 연구와 후속 조치 (도움을 주셔서 감사합니다!)와 스프링 코드에 대한 소량의 런타임 추적이 없으면 결국 모든 예외 (오류가 아니라 읽기)를 처리하는 구성을 찾았습니다. 404 포함.

1 단계- “처리기를 찾을 수 없음”상황에서 MVC 사용을 중지하도록 SpringBoot에 지시하십시오. 우리는 클라이언트에게 뷰 리다이렉션을 “/ error”로 리턴하는 대신 Spring이 예외를 던지기를 원한다. 이렇게하려면 구성 클래스 중 하나에 항목이 있어야합니다.

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

이것의 단점은 기본 디스패처 서블릿을 대체한다는 것입니다. 부작용이나 실행 문제가 나타나지 않고 아직 문제가되지 않았습니다. 다른 이유로 디스패처 서블릿을 사용하여 다른 작업을 수행하려는 경우이 작업을 수행 할 수 있습니다.

2 단계- 이제 핸들러가 발견되지 않으면 스프링 부트에서 예외가 발생하므로 해당 예외는 통합 예외 핸들러에서 다른 예외와 함께 처리 될 수 있습니다.

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

여기서는 “@EnableWebMvc”주석이 중요하다고 생각합니다. 이것 없이는 아무것도 작동하지 않는 것 같습니다. 이제 스프링 부트 앱이 위의 핸들러 클래스에서 404를 포함한 모든 예외를 포착하고 원하는대로 처리 할 수 ​​있습니다.

마지막 요점-던진 오류를 잡을 수있는 방법이없는 것 같습니다. 나는 측면을 사용하여 오류를 잡아서 위의 코드가 처리 할 수있는 예외로 바꾸는 것에 대한 엉뚱한 아이디어를 가지고 있지만 실제로 구현하려고 시도 할 시간이 없었습니다. 이것이 누군가를 돕기를 바랍니다.

모든 의견 / 수정 / 향상을 부탁드립니다.


답변

SpringBoot 1.4 이상에서는 더 쉬운 예외 처리를위한 새로운 쿨 클래스가 추가되어 상용구 코드를 제거하는 데 도움이되었습니다.

새로운는 @RestControllerAdvice예외 처리를 위해 제공되는데, 그것은의 조합 @ControllerAdvice@ResponseBody. 당신은 제거 할 수 @ResponseBody@ExceptionHandler이 새로운 주석을 사용할 때 방법.

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) {
        return new ApiErrorResponse(...);
    }
}

@EnableWebMvcapplication.properties에 주석과 다음을 추가하는 404 오류를 처리 하기에 충분했습니다.
spring.mvc.throw-exception-if-no-handler-found=true

https://github.com/magiccrafter/spring-boot-exception-handling 에서 소스를 찾아서 재생할 수 있습니다.


답변

나는 ResponseEntityExceptionHandler당신의 요구 사항을 충족 생각 합니다. HTTP 400 용 샘플 코드 :

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ExceptionHandler({HttpMessageNotReadableException.class, MethodArgumentNotValidException.class,
      HttpRequestMethodNotSupportedException.class})
  public ResponseEntity<Object> badRequest(HttpServletRequest req, Exception exception) {
    // ...
  }
}

게시물을 확인할 수 있습니다


답변

이것은 오래된 질문이지만 이것에 대한 생각을 나누고 싶습니다. 나는 그것이 당신 중 일부에게 도움이되기를 바랍니다.

현재 Spring Framework 4.3.7.RELEASE와 함께 Spring Boot 1.5.2.RELEASE를 사용하는 REST API를 작성 중입니다. XML 구성이 아닌 Java Config 접근 방식을 사용합니다. 또한 내 프로젝트는 @RestControllerAdvice주석을 사용하는 전역 예외 처리 메커니즘을 사용합니다 (나중에 참조).

내 프로젝트는 요구 사항과 동일합니다. REST API가 HTTP 404 Not Found존재하지 않는 URL에 요청을 보내려고 할 때 API 클라이언트에 대한 HTTP 응답의 JSON 페이로드와 함께 를 반환하기를 원합니다 . 필자의 경우 JSON 페이로드는 다음과 같습니다 (Spring Boot 기본값 인 btw와 분명히 다릅니다).

{
    "code": 1000,
    "message": "No handler found for your request.",
    "timestamp": "2017-11-20T02:40:57.628Z"
}

나는 마침내 그것을 작동시켰다. 다음은 간단하게 수행해야 할 주요 작업입니다.

  • NoHandlerFoundExceptionAPI 클라이언트가 핸들러 메소드가없는 URL을 호출하는 경우이 메소드가 처리 되는지 확인하십시오 (아래 1 단계 참조).
  • ApiErrorAPI 클라이언트에 반환해야하는 모든 데이터를 포함 하는 사용자 정의 오류 클래스 (필자의 경우 )를 작성하십시오 (2 단계 참조).
  • 에 반응 NoHandlerFoundException
    하고 API 클라이언트에 적절한 오류 메시지를 반환 하는 예외 처리기를 만듭니다 (3 단계 참조).
  • 테스트를 작성하고 작동하는지 확인하십시오 (4 단계 참조).

이제 세부 사항으로 넘어갑니다.

1 단계 : application.properties 구성

프로젝트 application.properties파일에 다음 두 가지 구성 설정을 추가해야했습니다 .

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

이것은 NoHandlerFoundException클라이언트가 요청을 처리 할 수있는 컨트롤러 메소드가없는 URL에 액세스하려고 할 때 발생합니다.

2 단계 : API 오류에 대한 클래스 만들기

Eugen Paraschiv의 블로그 에서이 기사 에서 제안한 것과 비슷한 수업을 만들었습니다 . 이 클래스는 API 오류를 나타냅니다. 이 정보는 오류가 발생할 경우 HTTP 응답 본문에서 클라이언트로 전송됩니다.

public class ApiError {

    private int code;
    private String message;
    private Instant timestamp;

    public ApiError(int code, String message) {
        this.code = code;
        this.message = message;
        this.timestamp = Instant.now();
    }

    public ApiError(int code, String message, Instant timestamp) {
        this.code = code;
        this.message = message;
        this.timestamp = timestamp;
    }

    // Getters and setters here...
}

3 단계 : 전역 예외 처리기 생성 / 구성

다음 클래스를 사용하여 예외를 처리합니다 (간단하게하기 위해 import 문, 로깅 코드 및 기타 관련없는 코드 조각을 제거했습니다).

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiError noHandlerFoundException(
            NoHandlerFoundException ex) {

        int code = 1000;
        String message = "No handler found for your request.";
        return new ApiError(code, message);
    }

    // More exception handlers here ...
}

4 단계 : 테스트 작성

API가 실패한 경우에도 항상 올바른 오류 메시지를 호출 클라이언트에 반환하고 싶습니다. 따라서 다음과 같은 테스트를 작성했습니다.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SprintBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("dev")
public class GlobalExceptionHandlerIntegrationTest {

    public static final String ISO8601_DATE_REGEX =
        "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$";

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser(roles = "DEVICE_SCAN_HOSTS")
    public void invalidUrl_returnsHttp404() throws Exception {
        RequestBuilder requestBuilder = getGetRequestBuilder("/does-not-exist");
        mockMvc.perform(requestBuilder)
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.code", is(1000)))
            .andExpect(jsonPath("$.message", is("No handler found for your request.")))
            .andExpect(jsonPath("$.timestamp", RegexMatcher.matchesRegex(ISO8601_DATE_REGEX)));
    }

    private RequestBuilder getGetRequestBuilder(String url) {
        return MockMvcRequestBuilders
            .get(url)
            .accept(MediaType.APPLICATION_JSON);
    }

@ActiveProfiles("dev")주석 거리에 둘 수 있습니다. 다른 프로파일로 작업 할 때만 사용합니다. 은 RegexMatcher사용자 정의입니다 Hamcrest의 정규 내가 더 잘 핸들 소인 필드에 사용합니다. 여기에 코드 (나는 그것을 발견의 여기 )

public class RegexMatcher extends TypeSafeMatcher<String> {

    private final String regex;

    public RegexMatcher(final String regex) {
        this.regex = regex;
    }

    @Override
    public void describeTo(final Description description) {
        description.appendText("matches regular expression=`" + regex + "`");
    }

    @Override
    public boolean matchesSafely(final String string) {
        return string.matches(regex);
    }

    // Matcher method you can call on this matcher class
    public static RegexMatcher matchesRegex(final String string) {
        return new RegexMatcher(regex);
    }
}

내 편에서 몇 가지 추가 메모 :

  • StackOverflow의 다른 많은 게시물에서 사람들은 @EnableWebMvc주석 설정을 제안했습니다 . 내 경우에는 필요하지 않았습니다.
  • 이 방법은 MockMvc와 잘 작동합니다 (위 테스트 참조).

답변

이 코드는 어떻습니까? 폴백 요청 매핑을 사용하여 404 오류를 포착합니다.

@Controller
@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        //If exception has a ResponseStatus annotation then use its response code
        ResponseStatus responseStatusAnnotation = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);

        return buildModelAndViewErrorPage(request, response, ex, responseStatusAnnotation != null ? responseStatusAnnotation.value() : HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @RequestMapping("*")
    public ModelAndView fallbackHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return buildModelAndViewErrorPage(request, response, null, HttpStatus.NOT_FOUND);
    }

    private ModelAndView buildModelAndViewErrorPage(HttpServletRequest request, HttpServletResponse response, Exception ex, HttpStatus httpStatus) {
        response.setStatus(httpStatus.value());

        ModelAndView mav = new ModelAndView("error.html");
        if (ex != null) {
            mav.addObject("title", ex);
        }
        mav.addObject("content", request.getRequestURL());
        return mav;
    }

}


답변

기본적으로 Spring Boot는 json에 오류 세부 정보를 제공합니다.

curl -v localhost:8080/greet | json_pp
[...]
< HTTP/1.1 400 Bad Request
[...]
{
   "timestamp" : 1413313361387,
   "exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
   "status" : 400,
   "error" : "Bad Request",
   "path" : "/greet",
   "message" : "Required String parameter 'name' is not present"
}

모든 종류의 요청 매핑 오류에도 적용됩니다. 이 기사를 확인하십시오
http://www.jayway.com/2014/10/19/spring-boot-error-responses/

로그를 작성하려면 NoSQL에 로그하십시오. @ControllerAdvice를 작성하여 로그 한 다음 예외를 다시 발생시킬 수 있습니다. https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc 문서에 예가
있습니다.


답변

@RestControllerAdvice는 Spring Framework 4.3의 새로운 기능으로 Cross-cutting 우려 솔루션으로 RestfulApi의 예외를 처리합니다.

 package com.khan.vaquar.exception;

import javax.servlet.http.HttpServletRequest;

import org.owasp.esapi.errors.IntrusionException;
import org.owasp.esapi.errors.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.khan.vaquar.domain.ErrorResponse;

/**
 * Handles exceptions raised through requests to spring controllers.
 **/
@RestControllerAdvice
public class RestExceptionHandler {

    private static final String TOKEN_ID = "tokenId";

    private static final Logger log = LoggerFactory.getLogger(RestExceptionHandler.class);

    /**
     * Handles InstructionExceptions from the rest controller.
     *
     * @param e IntrusionException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IntrusionException.class)
    public ErrorResponse handleIntrusionException(HttpServletRequest request, IntrusionException e) {
        log.warn(e.getLogMessage(), e);
        return this.handleValidationException(request, new ValidationException(e.getUserMessage(), e.getLogMessage()));
    }

    /**
     * Handles ValidationExceptions from the rest controller.
     *
     * @param e ValidationException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ValidationException.class)
    public ErrorResponse handleValidationException(HttpServletRequest request, ValidationException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);

        if (e.getUserMessage().contains("Token ID")) {
            tokenId = "<OMITTED>";
        }

        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(),
                                    e.getClass().getSimpleName(),
                                    e.getUserMessage());
    }

    /**
     * Handles JsonProcessingExceptions from the rest controller.
     *
     * @param e JsonProcessingException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = JsonProcessingException.class)
    public ErrorResponse handleJsonProcessingException(HttpServletRequest request, JsonProcessingException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(),
                                    e.getClass().getSimpleName(),
                                    e.getOriginalMessage());
    }

    /**
     * Handles IllegalArgumentExceptions from the rest controller.
     *
     * @param e IllegalArgumentException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public ErrorResponse handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(),
                                    e.getClass().getSimpleName(),
                                    e.getMessage());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = UnsupportedOperationException.class)
    public ErrorResponse handleUnsupportedOperationException(HttpServletRequest request, UnsupportedOperationException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(),
                                    e.getClass().getSimpleName(),
                                    e.getMessage());
    }

    /**
     * Handles MissingServletRequestParameterExceptions from the rest controller.
     *
     * @param e MissingServletRequestParameterException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ErrorResponse handleMissingServletRequestParameterException( HttpServletRequest request,
                                                                        MissingServletRequestParameterException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(),
                                    e.getClass().getSimpleName(),
                                    e.getMessage());
    }

    /**
     * Handles NoHandlerFoundExceptions from the rest controller.
     *
     * @param e NoHandlerFoundException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public ErrorResponse handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.NOT_FOUND.value(),
                                    e.getClass().getSimpleName(),
                                    "The resource " + e.getRequestURL() + " is unavailable");
    }

    /**
     * Handles all remaining exceptions from the rest controller.
     *
     * This acts as a catch-all for any exceptions not handled by previous exception handlers.
     *
     * @param e Exception
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public ErrorResponse handleException(HttpServletRequest request, Exception e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.error(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.INTERNAL_SERVER_ERROR.value(),
                                    e.getClass().getSimpleName(),
                                    "An internal error occurred");
    }

}