SpringBoot - 다양한 Content - Type 을 동시에 매개 변수 바 인 딩 처리 방법
SpringBoot 버 전 2.1.1 - RELEASE.작업 중 에 이러한 특수 한 수 요 를 만 났 습 니 다. 프론트 데스크 에서 들 어 오 는 인 자 를 받 고 인 자 를 받 으 며 대상 을 밀봉 한 후에 후속 적 인 처 리 를 해 야 합 니 다.기 존 논리 에 따 르 면 프론트 데스크 톱 에서 http 인 터 페 이 스 를 요청 하 는 Content - type 은 두 가지 가 있 습 니 다. application / json 과 application / x - ww - form - urlencoded 입 니 다.현재 두 가지 요청 방식 이 모두 매개 변수 바 인 딩 을 할 수 있 도록 요구 합 니 다.Handler MethodArgument Resolver 를 사용자 정의 해서 실현 하고 싶 습 니 다.
2. 매개 변수 바 인 딩 의 원리
테스트 코드 1:
@RestController
@RequestMapping("/test")
@Slf4j
public class TestWebController {
@RequestMapping("/form")
public Person testFormData(Person person, HttpServletRequest request) {
String contentType = request.getHeader("content-type");
log.info("Content-Type:" + contentType);
log.info(" :{}", JSON.toJSONString(person));
return person;
}
}
요청 인자:
curl --request POST \
--url http://localhost:8080/test/form \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'name=test&age=19'
콘 솔 출력 결과:
TestWebController : Content-Type:application/x-www-form-urlencoded
TestWebController : :{"age":19,"name":"test"}
폼 이 제출 한 매개 변 수 는 필드 이름 에 따라 Person 대상 에 자동 으로 연결 되 어 있 음 을 알 수 있 습 니 다.
원본 코드 를 보면 http 요청 이 Dispatcher Servlet 에 들 어 가 는 doDispatch 방법 을 알 수 있 습 니 다. 방법 을 통 해
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
현재 요청 한 RequestMappingHandlerAdapter 대상 ha 를 가 져 온 후 방법 을 실 행 했 습 니 다.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
이 방법 에서 AbstractHandler MethodAdapter 추상 류 의 기본 방법 handle 을 실 행 했 고 기본 방법 은 ha 의 handle Internal 방법 을 호출 했다.그 다음 에 방법 형 참 을 통 해 들 어 오 는 HandlerMethod 대상 (HandlerMethod 대상 은 사실 우리 Controller 에서 직접 쓴 testformData 의 Method 대상) 을 통 해 실행 가능 한 방법 인 Invocable HandlerMethod 를 얻 을 수 있 습 니 다.이 어 실행 가능 한 방법 대상 의 getMethodArgument Values 방법 을 실 행 했 습 니 다.
MethodParameter[] parameters = getMethodParameters();
방법 에서 현재 방법의 모든 형 삼 을 얻 었 다.그 다음 에 이 형 삼 을 반복 해서 옮 겨 다 니 며 HandlerMethodArgument Resolver 인터페이스의 실현 류 를 통 해 이 형 삼 을 처리 합 니 다.여기 서 우 리 는 현재 의 resolvers 가 하나의 조합 대상 이라는 것 을 발견 했다.이 조합 대상 도 이 인 터 페 이 스 를 실 현 했 고 이 대상 은 개인 적 인 구성원 변수 가 있 습 니 다. 인터페이스의 실현 류 의 집합 입 니 다.파 라 메 터 를 처리 할 때 현재 resolver 의 집합 을 옮 겨 다 니 며 인터페이스 방법 슈퍼 ports Parameter 를 통 해 현재 인삼 의 MethodParameter 대상 을 검증 합 니 다.true 로 돌 아 왔 을 때 현재 resolver 가 현재 의 형 삼 을 지원 한 다 는 것 을 증명 하고 현재 의 resolver 를 선택 하여 현재 의 형 삼 을 처리 합 니 다.해당 resolver 에 처음 일치 한 후에 메모리 등급 의 캐 시 를 진행 합 니 다.다음 에 같은 유형의 인삼 을 resolver 로 선택 할 때 집합 을 옮 겨 다 니 지 않 습 니 다.
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports
* the given method parameter.
*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
해당 resolver 를 선택 한 후 방법 을 통 해 들 어 오 는 request 대상 은 resolver 의 resolve Argument 방법 을 실행 하여 형 삼 의 값 을 봉 합 니 다.
조합 대상 을 관찰 한 결과 26 개의 내 장 된 대상 이 각각 장면 에 따라 형 삼 의 처 리 를 담당 하 는 것 으로 나 타 났 다.여기 에는 왜 controller 의 형 삼 위치 에 HttpServletRequest, HttpServletResponse 등 대상 을 자동 으로 주입 하 는 지 설명 한다.
실행 과정 을 관찰 한 결과, Content - type 이 application / x - ww - form - urlencoded 일 때, 형 삼 을 처리 하 는 resolver 는 ServletModelAttributeMethodProcessor 임 을 발견 하 였 다.
테스트 코드 2:
@RequestMapping("/entity")
public Person testFromEntity(@RequestBody Person person, HttpServletRequest request) {
String contentType = request.getHeader("content-type");
log.info("Content-Type:" + contentType);
log.info(" :{}", JSON.toJSONString(person));
return person;
}
요청 인자:
curl --request GET \
--url http://localhost:8080/test/entity \
--header 'Content-Type: application/json' \
형 삼 이 @ RequestBody 주석 에 표 시 될 때 요청 체 에 들 어 오지 않 으 면 오 류 를 보고 하 는 것 을 발견 할 수 있 습 니 다.위 와 같은 절 차 를 통 해 형 삼 이 @ RequestBody 주석 으로 표 시 될 때 SpringBoot 에서 선택 한 resolver 는 RequestResponse Body MethodProcessor 임 을 알 수 있 습 니 다.
요청 체 를 통 해 제 이 슨 에 게 전 달 될 때:
curl --request GET \
--url http://localhost:8080/test/entity \
--header 'Content-Type: application/json' \
--data '{
"name": "json",
"age": 20
}'
관찰 할 수 있다
TestWebController : Content-Type:application/json
TestWebController : :{"age":20,"name":"json"}
콘 솔 에서 성공 적 으로 연 결 된 인 자 를 출력 하 였 습 니 다.
또한, argument Resolvers 집합 을 관찰 한 결과 RequestResponse Body MethodProcessor 의 순 서 는 ServletModelAttributeMethodProcessor 보다 훨씬 높 고, ServletModelAttributeMethodProcessor 는 마지막 resolver 인 것 으로 나 타 났 다.
따라서 @ RequestBody 주석 이 표 시 된 형 삼 은 ServletModelAttribute MethodProcessor 를 통 해 데이터 바 인 딩 을 실현 할 기회 가 없 을 것 입 니 다. url 이후 주소 연결 매개 변 수 를 통 해 서버 에 요청 하 더 라 도.비어 있 거나 해석 할 수 없 는 제 이 슨 에 들 어 갈 때 400 의 오류 에 직접 응답 합 니 다.
3. 사용자 정의 PostEntityHandlerMethodArgument Resolver
상기 테스트 를 통 해 형 삼 파라미터 연결 을 처리 하 는 resolver 는 모두 HandlerMethodArgument Resolver 인터페이스의 실현 클래스 임 을 알 수 있 습 니 다.그래서 저 는 이러한 실현 류 를 사용자 정의 하여 우리 가 처리 해 야 할 형 삼 에 대해 매개 변수 바 인 딩 처 리 를 할 생각 입 니 다.
사용자 정의 resolver 를 새로 만 들 고 인 터 페 이 스 를 실현 한 후에 두 가지 방법 이 필요 합 니 다. 슈퍼 ports Parameter 와 resolveArgument 입 니 다.
슈퍼 ports Parameter 방법 은 resolver 조합 대상 이 형 삼 을 통 해 resolver 를 선택 할 때 판단 하 는 방법 입 니 다.이 방법 이 true 로 되 돌아 오 면 이 해석 기 를 대표 하여 이 유형의 형 참 을 처리 할 수 있 습 니 다.그렇지 않 으 면 false 로 돌아 가 다음 선택 을 반복 합 니 다.따라서 여기에서 성공 적 으로 포착 할 수 있 도록 사용자 정의 형 삼 을 표시 해 야 합 니 다.
내 방법 은 빈 인 터 페 이 스 를 사용자 정의 하 는 것 이다.
public interface PostEntity {
}
우리 의 실체 류 로 하여 금 이 인 터 페 이 스 를 실현 하 게 하지만, 아무것도 할 필요 가 없다. 슈퍼 ports Parameter 방법 에서 들 어 오 는 유형 이 PostEntity 의 실현 클래스 인지 판단 합 니 다.실현 클래스 라면 true 로 돌아 갑 니 다. 그렇지 않 으 면 false 로 돌아 가 다른 유형의 인삼 값 의 주입 에 영향 을 주지 않 습 니 다.
resolveArgument 방법 에 대해 서 는 Content - Type 에 따라 위 에서 언급 한 두 개의 resolver 를 직접 호출 하면 됩 니 다. 스스로 이 논 리 를 실현 할 필요 가 없습니다.또한 매개 변수 처리 전역 의 일치 성 을 확보 할 수 있다.Content - Type 에 의존 하 는 값 을 판단 하기 때문에 호출 자가 Content - Type 에 들 어가 야 합 니 다.
@Slf4j
@AllArgsConstructor
public class PostEntityHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;
private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;
private static final String APPLICATION_JSON = "application/json";
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class> parameterType = parameter.getParameterType();
String parameterName = parameter.getParameterName();
if (PostEntity.class.isAssignableFrom(parameterType)) {
log.info("name:{},type:{}", parameterName, parameterType.getName());
log.info("matched");
return true;
}
return false;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
assert request != null;
String contentType = request.getContentType();
log.debug("Content-Type:{}", contentType);
if (APPLICATION_JSON.equalsIgnoreCase(contentType)) {
return requestResponseBodyMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
} else {
return servletModelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
}
}
4. 사용자 정의 HandlerMethodArgument Resolver 등록
구조 가 완 료 된 후에 사용자 정의 resolver 를 resolver 의 조합 대상 에 추가 해 야 합 니 다.모든 사전 로드 resolvers 는 시작 과정 에서 Request Mapping HandlerAdapter 대상 에 설정 되 어 있 습 니 다.
WebMvcConfigurer 인 터 페 이 스 를 구현 하 는 설정 클래스 를 정의 하고 구성원 변수 위치 에 RequestMappingHandlerAdapter 대상 을 주입 합 니 다.주입 성공 을 확보 한 후 @ PostConstruct 의 init 방법 을 정의 합 니 다. 먼저 getArgument Resolvers 방법 으로 모든 resolvers 를 얻 은 다음 이 집합 을 옮 겨 다 니 며 우리 가 필요 로 하 는 두 개의 resolvers 를 얻 습 니 다.필요 한 인 자 를 가 져 온 후 사용자 정의 PostEntity Handler MethodArgument Resolver 를 구성 합 니 다.
resolver 집합 을 가 져 오 는 방법 소스 코드 를 보면 다음 과 같 습 니 다.
return Collections.unmodifiableList(this.argumentResolvers);
이 방법 이 되 돌아 오 는 집합 은 변 할 수 없 는 집합 으로 새로운 요 소 를 추가 할 방법 이 없다.따라서 우 리 는 새로운 집합 을 만들어 야 합 니 다. 크기 는 원래 의 집합 크기 + 1 이 고 사용자 정의 resolver 를 집합 첫 번 째 에 추가 한 다음 에 ha 대상 을 통 해 다시 설정 해 야 합 니 다.이렇게 하면 사용자 정의 resolver 등록 이 완 료 됩 니 다.
@Configuration
@Slf4j
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private RequestMappingHandlerAdapter ha;
private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor = null;
private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
@PostConstruct
private void init() {
List argumentResolvers = ha.getArgumentResolvers();
for (HandlerMethodArgumentResolver argumentResolver : argumentResolvers) {
if (argumentResolver instanceof ServletModelAttributeMethodProcessor) {
servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor) argumentResolver;
} else if (argumentResolver instanceof RequestResponseBodyMethodProcessor) {
requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) argumentResolver;
}
if (servletModelAttributeMethodProcessor != null && requestResponseBodyMethodProcessor != null) {
break;
}
}
PostEntityHandlerMethodArgumentResolver postEntityHandlerMethodArgumentResolver = new PostEntityHandlerMethodArgumentResolver(requestResponseBodyMethodProcessor, servletModelAttributeMethodProcessor);
List newList = new ArrayList<>(argumentResolvers.size() + 1);
newList.add(postEntityHandlerMethodArgumentResolver);
newList.addAll(argumentResolvers);
ha.setArgumentResolvers(newList);
}
}
결론
테스트 를 통 해 두 가지 요청 방식 이 모두 같은 방법 으로 형 삼 된 매개 변수 바 인 딩 을 실현 한 것 을 알 수 있다.이 기능 도 이렇게 복잡 한 실현 방식 없 이도 할 수 있 지만 이 문제 에 대한 연 구 를 통 해 spring 의 소스 코드 를 읽 고 매개 변수 바 인 딩 데이터 의 유통 과정 을 더욱 잘 알 게 되 었 다.
바 인 딩 이 필요 한 형 삼 은 외부 의존 vo 로 사용자 정의 인 터 페 이 스 를 실현 할 수 없고 사용자 정의 주 해 를 실현 할 수 있 으 며 사용자 정의 resolver 에서 도 포착 할 수 있 으 며 사용자 정의 처 리 를 할 수 있 습 니 다.
또 하나의 유용 한 장면 이 있 습 니 다. 이 방식 을 통 해 콘 텐 츠 - type 을 사용자 정의 하여 왜 그 랬 는 지 모 르 는 수 요 를 실현 할 수 있 습 니 다 ~
데모 코드:https://github.com/daegis/multi-content-type-demo
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.