/** * Validate the given type-level mapping metadata against the current request, * checking HTTP request method and parameter conditions. * @param mapping the mapping metadata to validate * @param request current HTTP request * @throws Exception if validation failed */ protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception { RequestMethod[] mappedMethods = mapping.method(); if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) { String[] supportedMethods = new String[mappedMethods.length]; for (int i = 0; i < mappedMethods.length; i++) { supportedMethods[i] = mappedMethods[i].name(); } throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods); } String[] mappedParams = mapping.params(); if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) { throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap()); } String[] mappedHeaders = mapping.headers(); if (!ServletAnnotationMappingUtils.checkHeaders(mappedHeaders, request)) { throw new ServletRequestBindingException("Header conditions \"" + StringUtils.arrayToDelimitedString(mappedHeaders, ", ") + "\" not met for actual request"); } }
@Test public void testUnsatisfiedServletRequestParameterException() throws Exception { try { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/params"); this.handlerMapping.getHandler(request); fail("UnsatisfiedServletRequestParameterException expected"); } catch (UnsatisfiedServletRequestParameterException ex) { List<String[]> groups = ex.getParamConditionGroups(); assertEquals(2, groups.size()); assertThat(Arrays.asList("foo=bar", "bar=baz"), containsInAnyOrder(groups.get(0)[0], groups.get(1)[0])); } }
/** * Client did not formulate a correct request. Log the exception message at warn level * and stack trace as trace level. Return response status HttpStatus.BAD_REQUEST * (400). * * @param e one of the exceptions, {@link MissingServletRequestParameterException}, * {@link UnsatisfiedServletRequestParameterException}, * {@link MethodArgumentTypeMismatchException}, or * {@link InvalidStreamDefinitionException} * @return the error response in JSON format with media type * application/vnd.error+json */ @ExceptionHandler({ MissingServletRequestParameterException.class, HttpMessageNotReadableException.class, UnsatisfiedServletRequestParameterException.class, MethodArgumentTypeMismatchException.class, InvalidStreamDefinitionException.class }) @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody public VndErrors onClientGenericBadRequest(Exception e) { String logref = logWarnLevelExceptionMessage(e); if (logger.isTraceEnabled()) { logTraceLevelStrackTrace(e); } String msg = getExceptionMessage(e); return new VndErrors(logref, msg); }
@Test public void testUnsatisfiedServletRequestParameterException() throws Exception { try { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/params"); this.handlerMapping.getHandler(request); fail("UnsatisfiedServletRequestParameterException expected"); } catch (UnsatisfiedServletRequestParameterException ex) { assertArrayEquals("Invalid request parameter conditions", new String[] { "foo=bar" }, ex.getParamConditions()); } }
@RequestMapping(produces = {ApplicationMediaType.APPLICATION_BEARCHOKE_V1_JSON_VALUE, ApplicationMediaType.APPLICATION_BEARCHOKE_V2_JSON_VALUE}) @ExceptionHandler({MissingServletRequestParameterException.class, UnsatisfiedServletRequestParameterException.class, HttpRequestMethodNotSupportedException.class, ServletRequestBindingException.class }) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public @ResponseBody ErrorMessage handleRequestException(Exception ex) { log.error("Http Status: " + HttpStatus.BAD_REQUEST + " : " + ex.getMessage(), ex); return new ErrorMessage(new Date(), HttpStatus.BAD_REQUEST.value(), ex.getClass().getName(), ex.getMessage()); }
@RequestMapping(produces = {Versions.V1_0, Versions.V2_0}) @ExceptionHandler({MissingServletRequestParameterException.class, UnsatisfiedServletRequestParameterException.class, HttpRequestMethodNotSupportedException.class, ServletRequestBindingException.class }) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public @ResponseBody Map<String, Object> handleRequestException(Exception ex) { Map<String, Object> map = Maps.newHashMap(); map.put("error", "Request Error"); map.put("cause", ex.getMessage()); return map; }
/** * Iterate all RequestMappingInfos once again, look if any match by URL at * least and raise exceptions accordingly. * @throws HttpRequestMethodNotSupportedException if there are matches by URL * but not by HTTP method * @throws HttpMediaTypeNotAcceptableException if there are matches by URL * but not by consumable/producible media types */ @Override protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos, String lookupPath, HttpServletRequest request) throws ServletException { Set<String> allowedMethods = new LinkedHashSet<String>(4); Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>(); Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>(); for (RequestMappingInfo info : requestMappingInfos) { if (info.getPatternsCondition().getMatchingCondition(request) != null) { patternMatches.add(info); if (info.getMethodsCondition().getMatchingCondition(request) != null) { patternAndMethodMatches.add(info); } else { for (RequestMethod method : info.getMethodsCondition().getMethods()) { allowedMethods.add(method.name()); } } } } if (patternMatches.isEmpty()) { return null; } else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) { throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods); } Set<MediaType> consumableMediaTypes; Set<MediaType> producibleMediaTypes; List<String[]> paramConditions; if (patternAndMethodMatches.isEmpty()) { consumableMediaTypes = getConsumableMediaTypes(request, patternMatches); producibleMediaTypes = getProducibleMediaTypes(request, patternMatches); paramConditions = getRequestParams(request, patternMatches); } else { consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches); producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches); paramConditions = getRequestParams(request, patternAndMethodMatches); } if (!consumableMediaTypes.isEmpty()) { MediaType contentType = null; if (StringUtils.hasLength(request.getContentType())) { try { contentType = MediaType.parseMediaType(request.getContentType()); } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } } throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes)); } else if (!producibleMediaTypes.isEmpty()) { throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes)); } else if (!CollectionUtils.isEmpty(paramConditions)) { throw new UnsatisfiedServletRequestParameterException(paramConditions, request.getParameterMap()); } else { return null; } }
@ExceptionHandler(value = Exception.class) @ResponseBody public String defaultErrorHandler(HttpServletRequest req, HttpServletResponse res, Exception e) throws Exception { String url = req != null ? req.getRequestURI() : ""; String estr_short = "ip=" + req.getRemoteAddr() + ", url=" + url; String estr_long = estr_short + ", e=" + e.toString(); boolean jsondemo = false; boolean jsonreq = (req != null && req.getHeader("X-Requested-With") != null) ? true : false; boolean dberror = false; if (e instanceof JsonResponseDemoSiteErrorException) { jsondemo = true; logger.debug(estr_long); } else if (e instanceof JsonResponseException) { logger.warn(estr_long); } // path variableの型不一致、Validationエラー、意図しないパラメータ(脆弱性を狙った攻撃) else if (e instanceof TypeMismatchException || e instanceof BindException || e instanceof UnsatisfiedServletRequestParameterException) { logger.info(estr_long); } // DB障害 else if (e instanceof MyBatisSystemException || e instanceof SQLException || e instanceof DataAccessException) { dberror = true; logger.info(estr_long); } else { logger.warn(estr_short, e); } if (jsondemo) { res.setStatus(HttpServletResponse.SC_OK); res.setContentType("application/json; charset=UTF-8"); return "{\"demoSite\":true}"; } else if (jsonreq) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); res.setContentType("application/json; charset=UTF-8"); return "{\"error\":true}"; } else if (dberror) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "db error"; } else { res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); res.setHeader("Location", contextPath); return ""; } }
/** * Iterate all RequestMappingInfos once again, look if any match by URL at * least and raise exceptions accordingly. * @throws HttpRequestMethodNotSupportedException if there are matches by URL * but not by HTTP method * @throws HttpMediaTypeNotAcceptableException if there are matches by URL * but not by consumable/producible media types */ @Override protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos, String lookupPath, HttpServletRequest request) throws ServletException { Set<String> allowedMethods = new LinkedHashSet<String>(4); Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>(); Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>(); for (RequestMappingInfo info : requestMappingInfos) { if (info.getPatternsCondition().getMatchingCondition(request) != null) { patternMatches.add(info); if (info.getMethodsCondition().getMatchingCondition(request) != null) { patternAndMethodMatches.add(info); } else { for (RequestMethod method : info.getMethodsCondition().getMethods()) { allowedMethods.add(method.name()); } } } } if (patternMatches.isEmpty()) { return null; } else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) { throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods); } Set<MediaType> consumableMediaTypes; Set<MediaType> producibleMediaTypes; Set<String> paramConditions; if (patternAndMethodMatches.isEmpty()) { consumableMediaTypes = getConsumableMediaTypes(request, patternMatches); producibleMediaTypes = getProducibleMediaTypes(request, patternMatches); paramConditions = getRequestParams(request, patternMatches); } else { consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches); producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches); paramConditions = getRequestParams(request, patternAndMethodMatches); } if (!consumableMediaTypes.isEmpty()) { MediaType contentType = null; if (StringUtils.hasLength(request.getContentType())) { try { contentType = MediaType.parseMediaType(request.getContentType()); } catch (IllegalArgumentException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } } throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes)); } else if (!producibleMediaTypes.isEmpty()) { throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes)); } else if (!CollectionUtils.isEmpty(paramConditions)) { String[] params = paramConditions.toArray(new String[paramConditions.size()]); throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap()); } else { return null; } }