Model has no value for key ‘aaa’ 라고 출력됐다

제목 그대로 오류가 발생했다. aaa는 임의로 설정한 키값이다.

일반적으로 스프링 @Controller에서 리다이렉트가 필요할 때, 아래와 같이 많이 쓴다.

public String myRedirect(Model model, HttpServletRequest req) {
   ...
	return "redirect:/api/path?" + req.getQueryString();
}

return에 redirect: 라고 문자열이 시작되면 그 다음에 오는 url로 리다이렉트가 된다. 이것은 스프링 공식문서에 보면 가이드가 되어있다.

차치하고, 저 리턴 부분에서 오류가 발생을 했다.

분석

먼저 리턴되는 문자열이 무엇인지 확인해봤다.

“redirect:/api/path?” + req.getQueryString()

=> “redirect:/api/path?query={aaa}

알고보니, 중괄호로 감싼 aaa를 모델에 키가 없다고 에러메시지를 주는거였다.

그렇다면 aaa를 키로 인식하는 이유가 있을 것이다.

RedirectView 내부를 살펴보면 아래와 같은 코드가 있다.

	protected StringBuilder replaceUriTemplateVariables(
			String targetUrl, Map<String, Object> model, Map<String, String> currentUriVariables, String encodingScheme)
			throws UnsupportedEncodingException {

		StringBuilder result = new StringBuilder();
		Matcher matcher = URI_TEMPLATE_VARIABLE_PATTERN.matcher(targetUrl);
		int endLastMatch = 0;
		while (matcher.find()) {
			String name = matcher.group(1);
			Object value = (model.containsKey(name) ? model.remove(name) : currentUriVariables.get(name));
			if (value == null) {
				throw new IllegalArgumentException("Model has no value for key '" + name + "'");
			}
			result.append(targetUrl, endLastMatch, matcher.start());
			result.append(UriUtils.encodePathSegment(value.toString(), encodingScheme));
			endLastMatch = matcher.end();
		}
		result.append(targetUrl.substring(endLastMatch));
		return result;
	}

어떤 패턴과 일치하면 키로 인식해서 처리해주는 로직으로 보인다.

URI_TEMPLATE_VARIABLE_PATTERN 가 패턴인데 아래와 같다.

private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");

정규식 패턴인데 {([^/]+?)} 가 뭐하는 녀석일까?

https://regex101.com

정규식 테스트 사이트로 이동해서 입력해본다.

java8로 설정하고 패턴부분을 붙여서 넣었다.

대충 살펴보면 중괄호가 있고 안에 그룹으로 / 가 아닌것들로 1개이상문자열과 매칭되는것 같다.

매칭되는 것들을 키로 인식하고 키에대한 벨류가 설정이 되어야 오류가 없을 것으로 보인다.

해결

키로 사용되는게 의도한 것이었다면, RedirectAttributes 를 인자로 받아서 addAttribute(“aaa”, 값); 을 설정해주면 오류가 해결되지만, 여기서는 단지 query 라는 키에 {aaa}라는 값을 가진 파라미터를 가진 URL로 리다이렉트를 하고 싶은거였다.

조금 더 상황설명을 상세하게 하면 리다이렉트를 하는 부분이 AOP로 처리된 부분이었는데 어떤 특정 조건에서 전달받은 모든 파라미터를 유지한 채 다른 URL로 리다이렉트를 하는 로직이다. 문제는 여기서 설정한 포인트컷에 해당되는 메소드 모두에 RedirectAttributes 를 추가 설정하는 것은 무리였다. 그래서 선택한 해결책은 아래와 같다.(Alan solved it.)

HttpServletResponse response= ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse();

response.sendRedirect("/api/path?" + req.getQueryString(););

그러면 그냥 닥치고 리다이렉트를 수행한다.

참고

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-redirecting-redirect-prefix

https://stackoverflow.com/questions/33417802/adding-redirectattributes-parameter-causes-model-has-no-value-for-key-exceptio