📡 백엔드/🌱 Spring Boot
[Spring] Swagger 연동 시 Unable to infer base url 접속 에러 (ResponseBodyAdvice)
gengminy
2022. 8. 11. 21:25
Unable to infer base url. This is common when using dynamic servlet registration or when the API is behind an API Gateway. The base url is the root of where all the swagger resources are served. For e.g. if the api is available at http://example.org/api/v2/api-docs then the base url is http://example.org/api/.
Please enter the location manually:
스웨거 사용 중인 스프링 부트 서버에서
기능 업그레이드 중 갑작스럽게 나타난 에러
원래는 이런 메세지 안떴는데 갑자기 떴다
구글링해도 다 뻔한 소리들 뿐이라서 좀 헤맸는데
다행스럽게 원인을 찾아냈다
💻 Project Dependency
...
id 'org.springframework.boot' version '2.7.1'
...
...
implementation 'io.springfox:springfox-boot-starter:3.0.0'
implementation 'io.springfox:springfox-swagger-ui:3.0.0'
...
Spring Boot 2.7.1 그리고 Spring fox 3.0 사용 중이다
다운 그레이드를 하면 된다 안된다 말도 있는데 설정만 잘해주면 상관이 없다
💻 설정 파일
📝 SwaggerConfig.java
@Configuration
@EnableWebMvc
public class SwaggerConfig {
private ApiInfo swaggerInfo() {
return new ApiInfoBuilder()
.title("HIFI Api Docs")
.version("0.0.1")
.description("HIFI Api 문서입니다")
.build();
}
@Bean
public Docket swaggerApi() {
return new Docket(DocumentationType.SWAGGER_2)
.securityContexts(Arrays.asList(securityContext()))
.securitySchemes(Arrays.asList(apiKey()))
.ignoredParameterTypes(AuthenticationPrincipal.class)
.consumes(getConsumeContentTypes())
.produces(getProduceContentTypes())
.apiInfo(swaggerInfo()).select()
.apis(RequestHandlerSelectors.basePackage("Backend.HIFI"))
.paths(PathSelectors.any())
.build();
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope(
"global",
"accessEverything"
);
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("Authorization", authorizationScopes));
}
private ApiKey apiKey() {
return new ApiKey("Authorization", "X-AUTH-TOKEN", "header");
}
private Set<String> getConsumeContentTypes() {
Set<String> consumes = new HashSet<>();
consumes.add("application/json;charset=UTF-8");
consumes.add("application/x-www-form-urlencoded");
return consumes;
}
private Set<String> getProduceContentTypes() {
Set<String> produces = new HashSet<>();
produces.add("application/json;charset=UTF-8");
return produces;
}
}
설정 파일도 이상이 없다
🔨 해결 방안 1 - 스프링 시큐리티 설정
📝 SecurityConfiguration.java
@EnableWebSecurity
@RequiredArgsConstructor
@Configuration
public class SecurityConfiguration {
private static final String[] SwaggerPatterns = {
"/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**"
};
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
...
...
.antMatchers(SwaggerPatterns).permitAll()
...
...
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
만약 스프링 시큐리티를 사용 중이라면
스프링 시큐리티 설정 파일에서 Swagger 가 사용 중인 url에서
보안 설정을 끄는 방법이 해결 방안 중 하나이다
저 위에 있는 4가지 url 패턴을 스프링 시큐리티에서 허용해주면 된다
"/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**"
하지만 보안 설정을 해줬음에도 오류는 사라지지 않아서 다시 검색하다가
해결책을 발견했다
🔨 해결 방안 2 - @ControllerAdvice 설정
📝 ResponceAdvice.java
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(
MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType)
{
return true;
}
@Override
public Object beforeBodyWrite(
Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response)
{
if (body instanceof ErrorResponse)
return ResponseUtil.onError((ErrorResponse) body);
return ResponseUtil.onSuccess(body);
}
}
모든 문제는 바로 이 ResponseBodyAdvice의 구현체로
@RestControllerAdvice 를 추가해서 발생했다
글로벌 익셉션 핸들러와 글로벌 리스폰스 핸들러를 구현한 이후부터 이 문제가 발생해서
바로 띠용했다
이유는 스웨거 응답까지 이 글로벌 리스폰스 핸들러를 거쳐서 가기 때문에
충돌이 발생하는 것이다
편하려고 구현한 controller advice 에 뒷통수를 세게 맞았다
해결책 중 하나는 basePackage를 설정해서
컨트롤러 범위를 지정해주는 것이다
@RestControllerAdvice(basePackages = {"Backend.HIFI.controller"})
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
...
}
이렇게 스웨거 설정 파일이 없는 디렉토리만 지정해주면 잘 된다
하지만 나는 도메인 기반 디렉토리 구조를 사용 중이라
더 좋은 해결방안을 고민해봐야겠다