[Spring] Spring boot yml 파일에서 한글 또는 특수문자로 된 key (map)을 받으려고 할 때 에러날 경우

|

사실 아래와 같이 map 으로 된 키를 한글로 받는 경우는 별로 없긴하다.
그런데 최근에 업무를 하면서 아래와 같은 케이스가 발생하여 해결해야 하는 상황에 놓인적이 있어 방법을 기억할 겸 공유한다. (아래 부분은 key 에 / 또는 특수문자가 들어갈 경우도 처리가 가능하다.)

예제

application.yml

jmlim:
  testMap:
    홍길동: test1
    아버지: test2

Configuration class

@Data
@ConfigurationProperties(prefix="jmlim")
public class JmlimProperties {
	private Map<String, String> testMap;
}

Test class

@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest(classes=DataManage.class)
public class JmlimTest extends BaseTransactionalTest{

	@Autowired
	private JmlimProperties jmlimConfig;
	
	@Test
	public void read() {
		System.out.println(jmlimConfig.toString());
	}
	
}

error Exception

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'jmlim.test-map' to java.util.Map<java.lang.String, java.lang.String>:

    Reason: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<java.lang.String, java.lang.String>]

Action:

Update your application's configuration

해결방법

application.yml 파일을 아래와 같이 수정

jmlim:
  testMap:
    "[홍길동]": test1
    "[아버지]": test2

결과

JmlimProperties(testMap={홍길동=test1, 아버지=test2})

참고자료 :

  • https://github.com/spring-projects/spring-boot/issues/13404

[Spring] Spring boot 에서 Filter 사용하기

|

Bean 등록

스프링부트에서는 web.xml 이 더 이상 사용되지 않아 서블릿이나 필터를 org.springframework.boot.web.servlet 의 RegistrationBean 을 통해 등록해야 한다.

형식

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {
  
  @Bean
  public FilterRegistrationBean getFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean(new HSTSFilter());
    // registrationBean.addUrlPatterns("/*"); // 서블릿 등록 빈 처럼 패턴을 지정해 줄 수 있다.
    return registrationBean;
  }
}

rest 형태의 API 호출 시 Transfer-Encoding: chunked 대신 Content-Length 정보 나오도록 하고 싶을 때 필터 사용 예제.

ShallowEtagHeaderFilter 를 필터 체인에 추가.

@Configuration
public class FilterConfig {

     @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterBean = new FilterRegistrationBean();
        filterBean.setFilter(new ShallowEtagHeaderFilter());
        filterBean.setUrlPatterns(Arrays.asList("*"));
        return filterBean;
    }
}

응답결과

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:sxp:8090
ETag: "05e7d49208ba5db71c04d5c926f91f382"
Content-Type: application/json;charset=UTF-8
Content-Length: 232
Date: Wed, 16 Dec 2015 06:53:09 GMT

참고자료 :

  • https://gs.saro.me/dev?tn=503
  • https://stackoverflow.com/questions/24156490/how-to-set-content-length-in-spring-mvc-rest-for-json

Windows에서 MongoDB 설치 후 cfg 파일을 통한 실행 시 'Unrecognized option: mp' 오류 발생문제.

|

최근 몽고디비 스터디를 하고 있고 로컬에서 설치 후 학습이 필요하여 Windows 10에 패키지로 설치를 하였다.

https://www.mongodb.com/download-center/community 의 msi 파일 설치. (version 4.2 msi 패키지 파일)

Win 10에 MongoDB 설치시 한번에 패키지 설치를 사용할 수 있다고 생각했는데 설치 후에 mongodb 서버 서비스가 활성화 되지 않는 문제가 발생했다. 몽고디비는 윈도우 패키지 설치 파일로 기본 드라이브가 아닌 다른 드라이브에 설치하였고 (F:) 실행 시 아래와 같은 에러가 발생했다.

F:\mongodb\bin>mongod -f mongod.cfg
Unrecognized option: mp
try 'mongod --help' for more information

실행하기전 mongod.cfg 파일에서 bind ip 만 기존 127.0.0.1 허용에서 0.0.0.0 으로 외부망에서도 전부 접근 가능하도록 옵션을 변경하였었다.


구글링 한 결과 문제는 mongod.cfg에 있음을 알았고 mongod.cfg를 연 후 파일의 마지막 줄에서 mp라는 단어가 있었다. mp에 대한 관련 정보는 공식 구성 파일 옵션에서 찾을 수 없어서 mp를 제거하고 Mongodb를 다시 시작하니 성공적으로 서비스가 올라왔다.

mongod.cfg 파일

# mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# Where and how to store data.
storage:
  dbPath: F:\mongodb\data
  journal:
    enabled: true
#  engine:
#  mmapv1:
#  wiredTiger:

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path:  F:\mongodb\log\mongod.log

# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0


#processManagement:

#security:

#operationProfiling:

#replication:

#sharding:

## Enterprise-Only Options:

#auditLog:

#snmp:
mp <-- 해당 문자 제거.

출처

  • https://medium.com/@sleo1104/啟動-mongodb-遇到錯誤-unrecognized-option-mp-27d9561fb96f

[ERROR] Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

|

배경:

  • 스프링 부트를 사용하고 있으며, 최신 내장형 탐캣은 7에서 8.5로 되었다고 가정한다.

요청 예

<BODY onload=f.submit()>
<FORM name=f method=post action='http://jmlim.aaa.com/authentication?jmlim[login]=1&jmlim[app]=72&jmlim[from]=testjmlim'>
</FORM>
</BODY>

요청을 받는 객체 및 컨트롤러 예

@Data
public class JmlimRequest {
    private Map<String, Object> jmlim;
}
@Controller
public class ApiController {
  /**
   */
  @RequestMapping("/authentication")
  public String index(@ModelAttribute JmlimRequest jmlimRequest) throws Exception {

   ....
   ....

  }
}

뭐.. 대충 위와 같은 형태로 post 로 요청을 하고 받는다면 아래와 같은 Exception 이 발생한다.

Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
 
java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:479) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.34.jar:8.5.34]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.34.jar:8.5.34]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) [tomcat-embed-core-8.5.34.jar:8.5.34]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.34.jar:8.5.34]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.34.jar:8.5.34]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.34.jar:8.5.34]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]

사실 위 요청을 애초에 javascript 의 encodeURI 함수를 통해 [ <–문자를 인코딩해서 보낼 수 있다면 위 문제는 발생하지 않는다.

만약 위 요청을 우리가 컨트롤 할 수 있는 상황이 아니라고 한번 더 가정해본다.

우선.. Tomcat 의 특정 버전 이상에서 RFC 3986 규격이 적용되었다고 한다.
RFC 3986에는 영어 문자(a-zA-Z), 숫자(0-9), -. ~4 특수 문자 및 모든 예약 문자만 허용된다.

위 에러는 특수문자 중 ([) 를 차단하면서 발생한 것인데,
수정하려면 아래 relaxQueryChars 옵션에 허용할 문자를 추가하거나 톰캣 버전을 다운그레이드 해야한다.

아래는 스프링 부트에서 해당 옵션을 추가하는 예 이다.

@Configuration
public class TomcatWebServerCustomizer
        implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    /**
     * 톰캣에 옵션 추가.
     *
     * @param factory
     */
    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addConnectorCustomizers((TomcatConnectorCustomizer)
                connector -> connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|}"));
    }
}

참고자료

  • https://stackoverflow.com/questions/51703746/setting-relaxedquerychars-for-embedded-tomcat/51703955#51703955
  • https://stackoverflow.com/questions/41053653/tomcat-8-is-not-able-to-handle-get-request-with-in-query-parameters
  • https://programmer.help/blogs/characters-are-defined-in-rfc-7230-and-rfc-3986.html

Spring RestController 에서 @PathVariable에 특수문자 포함 허용하기.

|

Spring RestController @PathVariable 사용하여 값을 넘겨받을 때 값에 “.” 나 특수문자가 포함되어 있으면 해당 문자를 포함하여 뒤 문자열이 전부 잘려서 들어온다.

문자가 잘리는 예시

1. 요청 URL

GET http://localhost:8080/test/jmlim@2ss@aa.ddd

2. 로직

@Slf4j
@RestController
public class TestController {
   @GetMapping("/test/{path}")
   public String notAllowSpecialCharacters(@PathVariable String path) {
	   log.info(path);
	   return path;
   }
}

3. 반환값

jmlim

특수문자포함 이후 문자열은 출력되지 않았다.

이걸 아래와 같이 바꿔주면 특수문자까지 포함하여 사용할 수 있다.

1. 특수문자 포함하여 출력

@Slf4j
@RestController
public class TestController {
 
   @GetMapping("/test/{path:.+}")
   public String allowSpecialCharacters(@PathVariable String path) {
	   log.info(path);
	   return path;
   }

   @GetMapping("/test/{path}")
   public String notAllowSpecialCharacters(@PathVariable String path) {
	   log.info(path);
	   return path;
   }

}

2. 반환값

jmlim@2ss@aa.ddd

특수문자까지 포함되어 출력되었음을 확인하였다.

참고자료 :

  • https://winmargo.tistory.com/202
  • https://stackoverflow.com/questions/16332092/spring-mvc-pathvariable-with-dot-is-getting-truncated
  • https://javamin.tistory.com/350