Merge branch 'develop' into service-test
diff --git a/dubbo-admin-backend/pom.xml b/dubbo-admin-backend/pom.xml
index 8b2b4b7..dd10133 100644
--- a/dubbo-admin-backend/pom.xml
+++ b/dubbo-admin-backend/pom.xml
@@ -123,7 +123,6 @@
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
         </dependency>
-
     </dependencies>
 
     <build>
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
index 491b35e..382e2b2 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
@@ -60,8 +60,8 @@
     @Value("${admin.config-center.username:}")
     private String username;
     @Value("${admin.config-center.password:}")
-
     private String password;
+
     private static String globalConfigPath = "config/dubbo/dubbo.properties";
 
     private static final Logger logger = LoggerFactory.getLogger(ConfigCenter.class);
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/RouteServiceImpl.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/RouteServiceImpl.java
index bd1272e..88d90c2 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/RouteServiceImpl.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/RouteServiceImpl.java
@@ -328,9 +328,9 @@
 
     private String getPath(String key, String type) {
         if (type.equals(Constants.CONDITION_ROUTE)) {
-            return prefix + Constants.PATH_SEPARATOR + key + Constants.PATH_SEPARATOR + "condition-route";
+            return prefix + Constants.PATH_SEPARATOR + key + Constants.PATH_SEPARATOR + "condition-router";
         } else {
-            return prefix + Constants.PATH_SEPARATOR + key + Constants.PATH_SEPARATOR + "tag-route";
+            return prefix + Constants.PATH_SEPARATOR + key + Constants.PATH_SEPARATOR + "tag-router";
         }
     }
 
diff --git a/dubbo-admin-backend/src/main/resources/application-test.properties b/dubbo-admin-backend/src/main/resources/application-test.properties
new file mode 100644
index 0000000..2363252
--- /dev/null
+++ b/dubbo-admin-backend/src/main/resources/application-test.properties
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# centers in dubbo2.7
+admin.registry.address=zookeeper://127.0.0.1:2182
+admin.config-center=zookeeper://127.0.0.1:2182
+admin.metadata.address=zookeeper://127.0.0.1:2182
diff --git a/dubbo-admin-backend/src/main/resources/log4j.properties b/dubbo-admin-backend/src/main/resources/log4j.properties
deleted file mode 100644
index e269697..0000000
--- a/dubbo-admin-backend/src/main/resources/log4j.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-log4j.rootCategory=INFO, stdout, file
-log4j.logger.org.apache=WARN
-
-
-# 控制台输出
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.target=System.out
-log4j.appender.stdout.Threshold=INFO
-log4j.appender.stdout.encoding=GBK
-log4j.appender.stdout.layout.ConversionPattern=%5p %c{2} - %m%n
-
-# 文件输出
-log4j.appender.file=org.apache.log4j.RollingFileAppender
-log4j.appender.file.file=dubbo-governance.log
-log4j.appender.file.Threshold=INFO
-log4j.appender.file.append=true
-log4j.appender.file.maxFileSize=10MB
-log4j.appender.file.maxBackupIndex=100
-log4j.appender.file.layout=org.apache.log4j.PatternLayout
-log4j.appender.file.layout.ConversionPattern=%d [%t] %-5p %C{6} (%F:%L) - %m%n
diff --git a/dubbo-admin-backend/src/main/resources/logback-spring.xml b/dubbo-admin-backend/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..0e01216
--- /dev/null
+++ b/dubbo-admin-backend/src/main/resources/logback-spring.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<configuration>
+    <property name="LOG_FILE" value="dubbo-governance.log"/>
+    <property name="LOG_FILE_MAX_HISTORY" value="7"/>
+
+    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
+    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
+    <include resource="org/springframework/boot/logging/logback/file-appender.xml"/>
+
+    <logger name="org.apache" level="WARN"/>
+
+    <appender name="FILE-LOG-ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+        <discardingThreshold>0</discardingThreshold>
+        <queueSize>2048</queueSize>
+        <appender-ref ref="FILE"/>
+    </appender>
+
+    <appender name="CONSOLE-LOG-ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+        <discardingThreshold>0</discardingThreshold>
+        <queueSize>2048</queueSize>
+        <appender-ref ref="CONSOLE"/>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="CONSOLE-LOG-ASYNC"/>
+        <appender-ref ref="FILE-LOG-ASYNC"/>
+    </root>
+
+</configuration>
diff --git a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java
new file mode 100644
index 0000000..a151d3b
--- /dev/null
+++ b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.admin;
+
+import java.io.IOException;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryOneTime;
+import org.apache.curator.test.TestingServer;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestTemplate;
+
+@ActiveProfiles("test")
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public abstract class AbstractSpringIntegrationTest {
+  protected RestTemplate restTemplate = (new TestRestTemplate()).getRestTemplate();
+
+  protected static TestingServer zkServer;
+  protected static CuratorFramework zkClient;
+
+  @Value("${local.server.port}")
+  protected int port;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    zkServer = new TestingServer(2182, true);
+    zkClient = CuratorFrameworkFactory.newClient(zkServer.getConnectString(), new RetryOneTime(2000));
+    zkClient.start();
+  }
+
+  @AfterClass
+  public static void afterClass() throws IOException {
+    zkClient.close();
+    zkServer.stop();
+  }
+
+  protected String url(final String path) {
+    return "http://localhost:" + port + path;
+  }
+}
diff --git a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/config/ConfigCenterTest.java b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/config/ConfigCenterTest.java
new file mode 100644
index 0000000..1be2ba9
--- /dev/null
+++ b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/config/ConfigCenterTest.java
@@ -0,0 +1,140 @@
+package org.apache.dubbo.admin.config;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.TestingServer;
+import org.apache.dubbo.admin.common.exception.ConfigurationException;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class ConfigCenterTest {
+
+    private String zkAddress;
+    private TestingServer zkServer;
+    private CuratorFramework zkClient;
+
+    @Before
+    public void setup() throws Exception {
+        int zkServerPort = NetUtils.getAvailablePort();
+        zkAddress = "zookeeper://127.0.0.1:" + zkServerPort;
+        zkServer = new TestingServer(zkServerPort, true);
+        zkClient = CuratorFrameworkFactory.builder().connectString(zkServer.getConnectString()).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
+        zkClient.start();
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        zkServer.close();
+        zkServer.stop();
+    }
+
+    @InjectMocks
+    private ConfigCenter configCenter;
+
+    @Test
+    public void testGetDynamicConfiguration() throws Exception {
+        // mock @value inject
+        ReflectionTestUtils.setField(configCenter, "configCenter", zkAddress);
+        ReflectionTestUtils.setField(configCenter, "group", "dubbo");
+        ReflectionTestUtils.setField(configCenter, "username", "username");
+        ReflectionTestUtils.setField(configCenter, "password", "password");
+
+        // config is null
+        configCenter.getDynamicConfiguration();
+
+        // config is registry address
+        zkClient.createContainers("/dubbo/config/dubbo/dubbo.properties");
+        zkClient.setData().forPath("/dubbo/config/dubbo/dubbo.properties", "dubbo.registry.address=zookeeper://test-registry.com:2181".getBytes());
+        configCenter.getDynamicConfiguration();
+        Object registryUrl = ReflectionTestUtils.getField(configCenter, "registryUrl");
+        assertNotNull(registryUrl);
+        assertEquals("test-registry.com", ((URL) registryUrl).getHost());
+
+        // config is meta date address
+        zkClient.setData().forPath("/dubbo/config/dubbo/dubbo.properties", "dubbo.metadata-report.address=zookeeper://test-metadata.com:2181".getBytes());
+        configCenter.getDynamicConfiguration();
+        Object metadataUrl = ReflectionTestUtils.getField(configCenter, "metadataUrl");
+        assertNotNull(metadataUrl);
+        assertEquals("test-metadata.com", ((URL) metadataUrl).getHost());
+
+        // config is empty
+        zkClient.setData().forPath("/dubbo/config/dubbo/dubbo.properties", "".getBytes());
+        ReflectionTestUtils.setField(configCenter, "registryUrl", null);
+        ReflectionTestUtils.setField(configCenter, "metadataUrl", null);
+        configCenter.getDynamicConfiguration();
+        assertNull(ReflectionTestUtils.getField(configCenter, "registryUrl"));
+        assertNull(ReflectionTestUtils.getField(configCenter, "metadataUrl"));
+
+        // configCenter is null
+        ReflectionTestUtils.setField(configCenter, "configCenter", null);
+        // registryAddress is not null
+        ReflectionTestUtils.setField(configCenter, "registryAddress", zkAddress);
+        configCenter.getDynamicConfiguration();
+        registryUrl = ReflectionTestUtils.getField(configCenter, "registryUrl");
+        assertNotNull(registryUrl);
+        assertEquals("127.0.0.1", ((URL) registryUrl).getHost());
+
+        // configCenter & registryAddress are null
+        try {
+            ReflectionTestUtils.setField(configCenter, "configCenter", null);
+            ReflectionTestUtils.setField(configCenter, "registryAddress", null);
+            configCenter.getDynamicConfiguration();
+            fail("should throw exception when configCenter, registryAddress are all null");
+        } catch (ConfigurationException e) {
+        }
+    }
+
+    @Test
+    public void testGetRegistry() throws Exception {
+        try {
+            configCenter.getRegistry();
+            fail("should throw exception when registryAddress is blank");
+        } catch (ConfigurationException e) {
+        }
+        assertNull(ReflectionTestUtils.getField(configCenter, "registryUrl"));
+
+        // mock @value inject
+        ReflectionTestUtils.setField(configCenter, "registryAddress", zkAddress);
+        ReflectionTestUtils.setField(configCenter, "group", "dubbo");
+        ReflectionTestUtils.setField(configCenter, "username", "username");
+        ReflectionTestUtils.setField(configCenter, "password", "password");
+
+        configCenter.getRegistry();
+        Object registryUrl = ReflectionTestUtils.getField(configCenter, "registryUrl");
+        assertNotNull(registryUrl);
+        assertEquals("127.0.0.1", ((URL) registryUrl).getHost());
+    }
+
+    @Test
+    public void testGetMetadataCollector() throws Exception {
+        // when metadataAddress is empty
+        ReflectionTestUtils.setField(configCenter, "metadataAddress", "");
+        configCenter.getMetadataCollector();
+        assertNull(ReflectionTestUtils.getField(configCenter, "metadataUrl"));
+
+        // mock @value inject
+        ReflectionTestUtils.setField(configCenter, "metadataAddress", zkAddress);
+        ReflectionTestUtils.setField(configCenter, "group", "dubbo");
+        ReflectionTestUtils.setField(configCenter, "username", "username");
+        ReflectionTestUtils.setField(configCenter, "password", "password");
+
+        configCenter.getMetadataCollector();
+        Object metadataUrl = ReflectionTestUtils.getField(configCenter, "metadataUrl");
+        assertNotNull(metadataUrl);
+        assertEquals("127.0.0.1", ((URL) metadataUrl).getHost());
+    }
+}
diff --git a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/controller/ServiceControllerTest.java b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/controller/ServiceControllerTest.java
new file mode 100644
index 0000000..7518675
--- /dev/null
+++ b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/controller/ServiceControllerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.admin.controller;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.apache.dubbo.admin.AbstractSpringIntegrationTest;
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.admin.model.dto.ServiceDTO;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistry;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class ServiceControllerTest extends AbstractSpringIntegrationTest {
+  @Autowired
+  private Registry registry;
+
+  @Before
+  public void setUp() throws InterruptedException {
+    final Set<URL> registered = ((AbstractRegistry) registry).getRegistered();
+    for (final URL url : registered) {
+      registry.unregister(url);
+    }
+    TimeUnit.SECONDS.sleep(1);
+  }
+
+  @Test
+  public void shouldGetAllServices() throws Exception {
+    final int num = 10;
+    for (int i = 0; i < num; i++) {
+      final String service = "org.apache.dubbo.admin.test.service" + i;
+      final URL url = i % 2 == 0
+          ? generateProviderServiceUrl("dubbo-admin", service)
+          : generateConsumerServiceUrl("dubbo-admin", service);
+      registry.register(url);
+    }
+    TimeUnit.SECONDS.sleep(1);
+
+    final ResponseEntity<Set<String>> response = restTemplate.exchange(
+        url("/api/{env}/services"), HttpMethod.GET, null,
+        new ParameterizedTypeReference<Set<String>>() {
+        }, "whatever"
+    );
+    assertThat(response.getStatusCode(), is(HttpStatus.OK));
+    assertThat(response.getBody(), hasSize(num));
+  }
+
+  @Test
+  public void shouldGetAllApplications() throws Exception {
+    final int num = 10;
+    for (int i = 0; i < num; i++) {
+      final String service = "org.apache.dubbo.admin.test.service";
+      final URL url = generateProviderServiceUrl("dubbo-admin-" + i, service);
+      registry.register(url);
+    }
+    TimeUnit.SECONDS.sleep(1);
+
+    final ResponseEntity<Set<String>> responseEntity = restTemplate.exchange(
+        url("/api/{env}/applications"), HttpMethod.GET, null,
+        new ParameterizedTypeReference<Set<String>>() {
+        }, "whatever"
+    );
+    assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
+    assertThat(responseEntity.getBody(), hasSize(num));
+  }
+
+  @Test
+  public void shouldFilterUsingPattern() throws InterruptedException {
+    final int num = 10;
+    for (int i = 0; i < num; i++) {
+      final String service = "org.apache.dubbo.admin.test.service" + i + ".pattern" + (i % 2);
+      final String application = "dubbo-admin";
+      registry.register(generateProviderServiceUrl(application, service));
+    }
+    TimeUnit.SECONDS.sleep(1);
+
+    ResponseEntity<Set<ServiceDTO>> responseEntity = restTemplate.exchange(
+        url("/api/{env}/service?pattern={pattern}&filter={filter}"),
+        HttpMethod.GET, null, new ParameterizedTypeReference<Set<ServiceDTO>>() {
+        }, "whatever", Constants.SERVICE, "*"
+    );
+    assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
+    assertThat(responseEntity.getBody(), hasSize(num));
+
+    responseEntity = restTemplate.exchange(
+        url("/api/{env}/service?pattern={pattern}&filter={filter}"),
+        HttpMethod.GET, null, new ParameterizedTypeReference<Set<ServiceDTO>>() {
+        }, "whatever", Constants.SERVICE, "*pattern0"
+    );
+    assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
+    assertThat(responseEntity.getBody(), hasSize(num / 2));
+
+    responseEntity = restTemplate.exchange(
+        url("/api/{env}/service?pattern={pattern}&filter={filter}"),
+        HttpMethod.GET, null, new ParameterizedTypeReference<Set<ServiceDTO>>() {
+        }, "whatever", Constants.SERVICE, "*pattern1"
+    );
+    assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
+    assertThat(responseEntity.getBody(), hasSize(num / 2));
+  }
+
+  private URL generateProviderServiceUrl(final String application, final String serviceName) {
+    return URL.valueOf("dubbo://127.0.0.1:20881/" + serviceName
+        + "?application=" + application
+        + "&interface=" + serviceName
+        + "&side=provider");
+  }
+
+  private URL generateConsumerServiceUrl(final String application, final String serviceName) {
+    return URL.valueOf("dubbo://127.0.0.1:20881/" + serviceName
+        + "?application=" + application
+        + "&interface=" + serviceName
+        + "&side=consumer");
+  }
+}
diff --git a/dubbo-admin-frontend/src/components/ServiceDetail.vue b/dubbo-admin-frontend/src/components/ServiceDetail.vue
index 544d556..caead8b 100644
--- a/dubbo-admin-frontend/src/components/ServiceDetail.vue
+++ b/dubbo-admin-frontend/src/components/ServiceDetail.vue
@@ -207,6 +207,17 @@
         return result.trim()
       }
     },
+    computed: {
+      area () {
+        return this.$i18n.locale
+      }
+    },
+    watch: {
+      area () {
+        this.setdetailHeaders()
+        this.setmetaHeaders()
+      }
+    },
     mounted: function () {
       this.setmetaHeaders()
       this.setdetailHeaders()
diff --git a/dubbo-admin-frontend/src/components/ServiceSearch.vue b/dubbo-admin-frontend/src/components/ServiceSearch.vue
index 6d42be3..1ddb566 100644
--- a/dubbo-admin-frontend/src/components/ServiceSearch.vue
+++ b/dubbo-admin-frontend/src/components/ServiceSearch.vue
@@ -169,6 +169,9 @@
         } else if (this.selected === 2) {
           return this.$t('appSearchHint')
         }
+      },
+      area () {
+        return this.$i18n.locale
       }
     },
     watch: {
diff --git a/dubbo-admin-frontend/src/components/governance/AccessControl.vue b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
index 054cbc4..0c59e0d 100644
--- a/dubbo-admin-frontend/src/components/governance/AccessControl.vue
+++ b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
@@ -462,6 +462,9 @@
   computed: {
     queryBy () {
       return this.$t('by') + this.$t(this.items[this.selected].title)
+    },
+    area () {
+      return this.$i18n.locale
     }
   },
   watch: {
diff --git a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
index 4d5d765..7db8fad 100644
--- a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
+++ b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
@@ -442,6 +442,9 @@
     computed: {
       queryBy () {
         return this.$t('by') + this.$t(this.items[this.selected].title)
+      },
+      area () {
+        return this.$i18n.locale
       }
     },
     watch: {
diff --git a/dubbo-admin-frontend/src/components/governance/Overrides.vue b/dubbo-admin-frontend/src/components/governance/Overrides.vue
index 3861dfa..b2bedc6 100644
--- a/dubbo-admin-frontend/src/components/governance/Overrides.vue
+++ b/dubbo-admin-frontend/src/components/governance/Overrides.vue
@@ -429,6 +429,9 @@
     computed: {
       queryBy () {
         return this.$t('by') + this.$t(this.items[this.selected].title)
+      },
+      area () {
+        return this.$i18n.locale
       }
     },
     watch: {
diff --git a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
index 68bc7c0..5f95b4e 100644
--- a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
@@ -437,6 +437,9 @@
     computed: {
       queryBy () {
         return this.$t('by') + this.$t(this.items[this.selected].title)
+      },
+      area () {
+        return this.$i18n.locale
       }
     },
     watch: {
diff --git a/dubbo-admin-frontend/src/components/governance/TagRule.vue b/dubbo-admin-frontend/src/components/governance/TagRule.vue
index dbda2f6..36434e3 100644
--- a/dubbo-admin-frontend/src/components/governance/TagRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/TagRule.vue
@@ -315,6 +315,11 @@
     created () {
       this.setHeight()
     },
+    computed: {
+      area () {
+        return this.$i18n.locale
+      }
+    },
     watch: {
       area () {
         this.setHeaders()
diff --git a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
index 1eccdf6..0bbcf5f 100644
--- a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
+++ b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
@@ -418,6 +418,9 @@
     computed: {
       queryBy () {
         return this.$t('by') + this.$t(this.items[this.selected].title)
+      },
+      area () {
+        return this.$i18n.locale
       }
     },
     watch: {
diff --git a/pom.xml b/pom.xml
index c858050..80a82b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,7 @@
         <commons-lang3-version>3.7</commons-lang3-version>
 		<dubbo-version>2.7.0-SNAPSHOT</dubbo-version>
 		<curator-version>2.12.0</curator-version>
-		<curator-test-version>2.12.0</curator-test-version>
+		<curator-test-version>4.1.0</curator-test-version>
 		<fastjson-version>1.2.46</fastjson-version>
 		<springfox-swagger-version>2.9.2</springfox-swagger-version>
 		<netty-version>4.1.30.Final</netty-version>