2018年8月26日 星期日

Spring Filter - RESTFUL Log

最直接的方式就在每個method內直接加入restful log

但這樣就太囉嗦

所以改用filter來處理這件事情

但由於request body只能被讀取一次的特性

所以filter要針對request動點手腳

  1. @WebFilter(
  2. filterName="testFilter",
  3. urlPatterns={"/testRest"}
  4. )
  5. public class SpringRequestBodyFilter extends OncePerRequestFilter{
  6. @Override
  7. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  8. throws ServletException, IOException {
  9.  
  10. BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
  11. System.out.println("body: " + requestWrapper.getBody());
  12.  
  13. filterChain.doFilter(requestWrapper, response);
  14. }
  15. }
  16.  
  17. public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{
  18. private final String body;
  19.  
  20. public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
  21. super(request);
  22. try(BufferedReader bufferedReader = request.getReader()){
  23. body = request.getReader().lines().collect(Collectors.joining());
  24. }
  25. }
  26.  
  27. @Override
  28. public ServletInputStream getInputStream() throws IOException {
  29. return new DelegatingServletInputStream(new ByteArrayInputStream(body.getBytes()));
  30. }
  31.  
  32. @Override
  33. public BufferedReader getReader() throws IOException {
  34. return new BufferedReader(new InputStreamReader(this.getInputStream()));
  35. }
  36.  
  37. public String getBody() {
  38. return this.body;
  39. }
  40. }
第10行將HttpServletRequest包裝成BodyReaderHttpServletRequestWrapper傳給filter

BodyReaderHttpServletRequestWrapper的功能就是先讀取request body並存到body欄位中
由於前面提到過request body只能讀取一次的特性
所以要重新建立inputStream和reader
若有用到inputStream或reader時才不會出現Stream closed的錯誤

第29行這邊我用org.springframework.mock.web.DelegatingServletInputStream
這個是用spring test的(要自己實作ServletInputStream也可以,但已有寫好的沒有不拿來用的理由)

完整程式可參考https://github.com/softmenlouis/springBoot-filter.git

Spring Filter

1.使用Java自己本身的Filter
  1. @SpringBootApplication
  2. @ServletComponentScan("test.annotation.filter")
  3. public class SptringAnnotationFilterApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SptringAnnotationFilterApplication.class, args);
  6. }
  7. }
  8.  
  9.  
  10. @WebFilter(
  11. filterName="testFilter",
  12. urlPatterns={"/testRest"}
  13. )
  14. public class SpringAnnotationFilter implements Filter{
  15. @Override
  16. public void init(FilterConfig filterConfig) throws ServletException {
  17.  
  18. }
  19.  
  20. @Override
  21. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  22. throws IOException, ServletException {
  23. // do something
  24.  
  25. filterChain.doFilter(request, response);
  26. }
  27.  
  28. @Override
  29. public void destroy() {
  30.  
  31. }
  32. }
SptringAnnotationFilterApplication
1. 設定@ServletComponentScan(filter的package)

SpringAnnotationFilter
1. implements javax.servlet.Filter
2. 設定@WebFilter(urlPatterns是設定哪些url要filter)
3. 改寫doFilter

這樣filter就可以生效

註:
若有多個filter時,在這邊使用Spring的@Order是沒有用的
真的要使用order的話可以參考下面第二種作法or使用package name去控制filter順序

2.使用Spring的FilterRegistrationBean
  1. @SpringBootApplication
  2. public class SpringBeanConfigFilterApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringBeanConfigFilterApplication.class, args);
  5. }
  6. }
  7.  
  8. @Configuration
  9. public class FilterConfig {
  10. @Bean
  11. public FilterRegistrationBean<Filter> registFilter() {
  12. FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
  13. registration.setFilter(new SpringBeanConfigFilter());
  14. registration.addUrlPatterns("/testRest");
  15. registration.setOrder(2);
  16. registration.setName("testFilter");
  17. return registration;
  18. }
  19.  
  20. @Bean
  21. public FilterRegistrationBean<Filter> registFilter2() {
  22. FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
  23. registration.setFilter(new SpringBeanConfigFilter2());
  24. registration.addUrlPatterns("/testRest");
  25. registration.setOrder(1);
  26. registration.setName("testFilter2");
  27. return registration;
  28. }
  29. }
  30.  
  31. public class SpringBeanConfigFilter implements Filter{
  32. @Override
  33. public void init(FilterConfig filterConfig) throws ServletException {
  34.  
  35. }
  36.  
  37. @Override
  38. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  39. throws IOException, ServletException {
  40. // do something
  41.  
  42. filterChain.doFilter(request, response);
  43. }
  44.  
  45. @Override
  46. public void destroy() {
  47.  
  48. }
  49. }
  50.  
  51. public class SpringBeanConfigFilter2 implements Filter{
  52. @Override
  53. public void init(FilterConfig filterConfig) throws ServletException {
  54.  
  55. }
  56.  
  57. @Override
  58. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
  59. throws IOException, ServletException {
  60. // do something
  61.  
  62. filterChain.doFilter(request, response);
  63. }
  64.  
  65. @Override
  66. public void destroy() {
  67.  
  68. }
  69. }

使用Spring bean的方式去做設定
註:此設定的方式可以設定filter order

完整程式可參考https://github.com/softmenlouis/springBoot-filter.git

2018年8月8日 星期三

Multiple MongoDB connectors

pom.xml配置
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. <version>2.0.4.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-data-mongodb</artifactId>
  9. <version>2.0.4.RELEASE</version>
  10. </dependency>

Application設定
  1. @SpringBootApplication(
  2. exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}
  3. )
  4. public class TestApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(TestApplication.class, args);
  7. }
  8. }
最重要的就是要設定 exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}

application.properties設定
  1. mongodb.connect1.uri=mongodb://自己的連線設定1
  2. mongodb.connect1.database=自己的database1
  3.  
  4. mongodb.connect2.uri=mongodb://自己的連線設定2
  5. mongodb.connect2.database=自己的database2

Configurer設定
  1. @Configuration
  2. public class TestConfigurer{
  3. @Bean
  4. @ConfigurationProperties(prefix = "mongodb.connect1")
  5. public MongoProperties getConnectionSetting1() {
  6. return new MongoProperties();
  7. }
  8.  
  9. @Bean
  10. @ConfigurationProperties(prefix = "mongodb.connect2")
  11. public MongoProperties getConnectionSetting2() {
  12. return new MongoProperties();
  13. }
  14.  
  15. @Bean(name="Template1")
  16. public MongoTemplate mongoTemplate1(){
  17. MongoProperties p = getConnectionSetting1();
  18. return genMongoTemplateWithMongoProperties(p);
  19. }
  20.  
  21. @Bean(name="Template2")
  22. public MongoTemplate mongoTemplate2(){
  23. MongoProperties p = getConnectionSetting2();
  24.  
  25. return genMongoTemplateWithMongoProperties(p);
  26. }
  27.  
  28. private MongoTemplate genMongoTemplateWithMongoProperties(MongoProperties p){
  29. MongoClientURI uri = new MongoClientURI(p.getUri());
  30. MongoClient client = new MongoClient(uri);
  31.  
  32. SimpleMongoDbFactory f = new SimpleMongoDbFactory(client, p.getDatabase());
  33. return new MongoTemplate(f);
  34. }
  35. }

Dao使用Template
  1. @Repository
  2. public class TestDao {
  3. @Autowired
  4. @Qualifier("Template1")
  5. private MongoTemplate mongoTemplate1;
  6.  
  7. @Autowired
  8. @Qualifier("Template2")
  9. private MongoTemplate mongoTemplate2;
  10. }

eclipse plus properties edit

eclipse properties編輯小工具 http://propedit.sourceforge.jp/index_en.html