diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml
index 0d90b1ad..beab90ee 100755
--- a/sentinel-adapter/pom.xml
+++ b/sentinel-adapter/pom.xml
@@ -28,6 +28,7 @@
+ * How to implement a forward sub-request in your action: + *
+ * initalRequest() {
+ * ModelAndView mav = new ModelAndView();
+ * mav.setViewName("another");
+ * return mav;
+ * }
+ *
+ *
+ * @author kaizi2009
+ * @since 1.7.1
+ */
+public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
+
+ public static final String SENTINEL_SPRING_WEB_CONTEXT_NAME = "sentinel_spring_web_context";
+ private static final String EMPTY_ORIGIN = "";
+
+ private final BaseWebMvcConfig baseWebMvcConfig;
+
+ public AbstractSentinelInterceptor(BaseWebMvcConfig config) {
+ AssertUtil.notNull(config, "BaseWebMvcConfig should not be null");
+ AssertUtil.assertNotBlank(config.getRequestAttributeName(), "requestAttributeName should not be blank");
+ this.baseWebMvcConfig = config;
+ }
+
+ /**
+ * @param request
+ * @param rcKey
+ * @param step
+ * @return reference count after increasing (initial value as zero to be increased)
+ */
+ private Integer increaseReferece(HttpServletRequest request, String rcKey, int step) {
+ Object obj = request.getAttribute(rcKey);
+
+ if (obj == null) {
+ // initial
+ obj = Integer.valueOf(0);
+ }
+
+ Integer newRc = (Integer)obj + step;
+ request.setAttribute(rcKey, newRc);
+ return newRc;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ try {
+ String resourceName = getResourceName(request);
+
+ if (StringUtil.isEmpty(resourceName)) {
+ return true;
+ }
+
+ if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
+ return true;
+ }
+
+ // Parse the request origin using registered origin parser.
+ String origin = parseOrigin(request);
+ String contextName = getContextName(request);
+ ContextUtil.enter(contextName, origin);
+ Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
+ request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
+ return true;
+ } catch (BlockException e) {
+ try {
+ handleBlockException(request, response, e);
+ } finally {
+ ContextUtil.exit();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Return the resource name of the target web resource.
+ *
+ * @param request web request
+ * @return the resource name of the target web resource.
+ */
+ protected abstract String getResourceName(HttpServletRequest request);
+
+ /**
+ * Return the context name of the target web resource.
+ *
+ * @param request web request
+ * @return the context name of the target web resource.
+ */
+ protected String getContextName(HttpServletRequest request) {
+ return SENTINEL_SPRING_WEB_CONTEXT_NAME;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+ Object handler, Exception ex) throws Exception {
+ if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), -1) != 0) {
+ return;
+ }
+
+ Entry entry = getEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName());
+ if (entry == null) {
+ // should not happen
+ RecordLog.warn("[{}] No entry found in request, key: {}",
+ getClass().getSimpleName(), baseWebMvcConfig.getRequestAttributeName());
+ return;
+ }
+
+ traceExceptionAndExit(entry, ex);
+ removeEntryInRequest(request);
+ ContextUtil.exit();
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+ ModelAndView modelAndView) throws Exception {
+ }
+
+ protected Entry getEntryInRequest(HttpServletRequest request, String attrKey) {
+ Object entryObject = request.getAttribute(attrKey);
+ return entryObject == null ? null : (Entry)entryObject;
+ }
+
+ protected void removeEntryInRequest(HttpServletRequest request) {
+ request.removeAttribute(baseWebMvcConfig.getRequestAttributeName());
+ }
+
+ protected void traceExceptionAndExit(Entry entry, Exception ex) {
+ if (entry != null) {
+ if (ex != null) {
+ Tracer.traceEntry(ex, entry);
+ }
+ entry.exit();
+ }
+ }
+
+ protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e)
+ throws Exception {
+ if (baseWebMvcConfig.getBlockExceptionHandler() != null) {
+ baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);
+ } else {
+ // Throw BlockException directly. Users need to handle it in Spring global exception handler.
+ throw e;
+ }
+ }
+
+ protected String parseOrigin(HttpServletRequest request) {
+ String origin = EMPTY_ORIGIN;
+ if (baseWebMvcConfig.getOriginParser() != null) {
+ origin = baseWebMvcConfig.getOriginParser().parseOrigin(request);
+ if (StringUtil.isEmpty(origin)) {
+ return EMPTY_ORIGIN;
+ }
+ }
+ return origin;
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java
new file mode 100644
index 00000000..37ffd703
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+import org.springframework.web.servlet.HandlerMapping;
+
+/**
+ * Spring Web MVC interceptor that integrates with Sentinel.
+ *
+ * @author kaizi2009
+ * @since 1.7.1
+ */
+public class SentinelWebInterceptor extends AbstractSentinelInterceptor {
+
+ private final SentinelWebMvcConfig config;
+
+ public SentinelWebInterceptor() {
+ this(new SentinelWebMvcConfig());
+ }
+
+ public SentinelWebInterceptor(SentinelWebMvcConfig config) {
+ super(config);
+ if (config == null) {
+ // Use the default config by default.
+ this.config = new SentinelWebMvcConfig();
+ } else {
+ this.config = config;
+ }
+ }
+
+ @Override
+ protected String getResourceName(HttpServletRequest request) {
+ // Resolve the Spring Web URL pattern from the request attribute.
+ Object resourceNameObject = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
+ if (resourceNameObject == null || !(resourceNameObject instanceof String)) {
+ return null;
+ }
+ String resourceName = (String) resourceNameObject;
+ UrlCleaner urlCleaner = config.getUrlCleaner();
+ if (urlCleaner != null) {
+ resourceName = urlCleaner.clean(resourceName);
+ }
+ // Add method specification if necessary
+ if (StringUtil.isNotEmpty(resourceName) && config.isHttpMethodSpecify()) {
+ resourceName = request.getMethod().toUpperCase() + ":" + resourceName;
+ }
+ return resourceName;
+ }
+
+ @Override
+ protected String getContextName(HttpServletRequest request) {
+ if (config.isWebContextUnify()) {
+ return super.getContextName(request);
+ }
+
+ return getResourceName(request);
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebTotalInterceptor.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebTotalInterceptor.java
new file mode 100644
index 00000000..e1751c50
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebTotalInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * The web interceptor for all requests, which will unify all URL as
+ * a single resource name (configured in {@link SentinelWebMvcTotalConfig}).
+ *
+ * @author kaizi2009
+ * @since 1.7.1
+ */
+public class SentinelWebTotalInterceptor extends AbstractSentinelInterceptor {
+
+ private final SentinelWebMvcTotalConfig config;
+
+ public SentinelWebTotalInterceptor(SentinelWebMvcTotalConfig config) {
+ super(config);
+ if (config == null) {
+ this.config = new SentinelWebMvcTotalConfig();
+ } else {
+ this.config = config;
+ }
+ }
+
+ public SentinelWebTotalInterceptor() {
+ this(new SentinelWebMvcTotalConfig());
+ }
+
+ @Override
+ protected String getResourceName(HttpServletRequest request) {
+ return config.getTotalResourceName();
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/BlockExceptionHandler.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/BlockExceptionHandler.java
new file mode 100644
index 00000000..7e9c87f6
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/BlockExceptionHandler.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * Handler for the blocked request.
+ *
+ * @author kaizi2009
+ */
+public interface BlockExceptionHandler {
+
+ /**
+ * Handle the request when blocked.
+ *
+ * @param request Servlet request
+ * @param response Servlet response
+ * @param e the block exception
+ * @throws Exception users may throw out the BlockException or other error occurs
+ */
+ void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
+
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/DefaultBlockExceptionHandler.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/DefaultBlockExceptionHandler.java
new file mode 100644
index 00000000..9b54974d
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/DefaultBlockExceptionHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+
+/**
+ * Default handler for the blocked request.
+ *
+ * @author kaizi2009
+ */
+public class DefaultBlockExceptionHandler implements BlockExceptionHandler {
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
+ // Return 429 (Too Many Requests) by default.
+ response.setStatus(429);
+
+ PrintWriter out = response.getWriter();
+ out.print("Blocked by Sentinel (flow limiting)");
+ out.flush();
+ out.close();
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/RequestOriginParser.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/RequestOriginParser.java
new file mode 100644
index 00000000..9bb34270
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/RequestOriginParser.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * The origin parser parses request origin (e.g. IP, user, appName) from HTTP request.
+ *
+ * @author kaizi2009
+ */
+public interface RequestOriginParser {
+
+ /**
+ * Parse the origin from given HTTP request.
+ *
+ * @param request HTTP request
+ * @return parsed origin
+ */
+ String parseOrigin(HttpServletRequest request);
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/UrlCleaner.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/UrlCleaner.java
new file mode 100644
index 00000000..8541d097
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/UrlCleaner.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;
+
+/**
+ * Unify the resource target.
+ *
+ * @author kaizi2009
+ */
+public interface UrlCleaner {
+
+ /**
+ * Unify the resource target.
+ *
+ * @param originUrl the original URL
+ * @return the unified resource name
+ */
+ String clean(String originUrl);
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java
new file mode 100644
index 00000000..e1bd1542
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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 com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
+
+/**
+ * Common base configuration for Spring Web MVC adapter.
+ *
+ * @author kaizi2009
+ * @since 1.7.1
+ */
+public abstract class BaseWebMvcConfig {
+
+ protected String requestAttributeName;
+ protected String requestRefName;
+ protected BlockExceptionHandler blockExceptionHandler;
+ protected RequestOriginParser originParser;
+
+ public String getRequestAttributeName() {
+ return requestAttributeName;
+ }
+
+ public void setRequestAttributeName(String requestAttributeName) {
+ this.requestAttributeName = requestAttributeName;
+ this.requestRefName = this.requestAttributeName + "-rc";
+ }
+
+ /**
+ * Paired with attr name used to track reference count.
+ *
+ * @return
+ */
+ public String getRequestRefName() {
+ return requestRefName;
+ }
+
+ public BlockExceptionHandler getBlockExceptionHandler() {
+ return blockExceptionHandler;
+ }
+
+ public void setBlockExceptionHandler(BlockExceptionHandler blockExceptionHandler) {
+ this.blockExceptionHandler = blockExceptionHandler;
+ }
+
+ public RequestOriginParser getOriginParser() {
+ return originParser;
+ }
+
+ public void setOriginParser(RequestOriginParser originParser) {
+ this.originParser = originParser;
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java
new file mode 100644
index 00000000..c94ff8c2
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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 com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
+
+/**
+ * @author kaizi2009
+ * @since 1.7.1
+ */
+public class SentinelWebMvcConfig extends BaseWebMvcConfig {
+
+ public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "$$sentinel_spring_web_entry_attr";
+
+ /**
+ * Specify the URL cleaner that unifies the URL resources.
+ */
+ private UrlCleaner urlCleaner;
+
+ /**
+ * Specify whether the URL resource name should contain the HTTP method prefix (e.g. {@code POST:}).
+ */
+ private boolean httpMethodSpecify;
+
+ /**
+ * Specify whether unify web context(i.e. use the default context name), and is true by default.
+ *
+ * @since 1.7.2
+ */
+ private boolean webContextUnify = true;
+
+ public SentinelWebMvcConfig() {
+ super();
+ setRequestAttributeName(DEFAULT_REQUEST_ATTRIBUTE_NAME);
+ }
+
+ public UrlCleaner getUrlCleaner() {
+ return urlCleaner;
+ }
+
+ public SentinelWebMvcConfig setUrlCleaner(UrlCleaner urlCleaner) {
+ this.urlCleaner = urlCleaner;
+ return this;
+ }
+
+ public boolean isHttpMethodSpecify() {
+ return httpMethodSpecify;
+ }
+
+ public SentinelWebMvcConfig setHttpMethodSpecify(boolean httpMethodSpecify) {
+ this.httpMethodSpecify = httpMethodSpecify;
+ return this;
+ }
+
+ public boolean isWebContextUnify() {
+ return webContextUnify;
+ }
+
+ public SentinelWebMvcConfig setWebContextUnify(boolean webContextUnify) {
+ this.webContextUnify = webContextUnify;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "SentinelWebMvcConfig{" +
+ "urlCleaner=" + urlCleaner +
+ ", httpMethodSpecify=" + httpMethodSpecify +
+ ", webContextUnify=" + webContextUnify +
+ ", requestAttributeName='" + requestAttributeName + '\'' +
+ ", blockExceptionHandler=" + blockExceptionHandler +
+ ", originParser=" + originParser +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcTotalConfig.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcTotalConfig.java
new file mode 100644
index 00000000..dc8b4af0
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcTotalConfig.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
+
+/**
+ * @author kaizi2009
+ * @since 1.7.1
+ */
+public class SentinelWebMvcTotalConfig extends BaseWebMvcConfig {
+
+ public static final String DEFAULT_TOTAL_RESOURCE_NAME = "spring-mvc-total-url-request";
+ public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "$$sentinel_spring_web_total_entry_attr";
+
+ private String totalResourceName = DEFAULT_TOTAL_RESOURCE_NAME;
+
+ public SentinelWebMvcTotalConfig() {
+ super();
+ setRequestAttributeName(DEFAULT_REQUEST_ATTRIBUTE_NAME);
+ }
+
+ public String getTotalResourceName() {
+ return totalResourceName;
+ }
+
+ public SentinelWebMvcTotalConfig setTotalResourceName(String totalResourceName) {
+ this.totalResourceName = totalResourceName;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "SentinelWebMvcTotalConfig{" +
+ "totalResourceName='" + totalResourceName + '\'' +
+ ", requestAttributeName='" + requestAttributeName + '\'' +
+ ", blockExceptionHandler=" + blockExceptionHandler +
+ ", originParser=" + originParser +
+ '}';
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/ResultWrapper.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/ResultWrapper.java
new file mode 100644
index 00000000..66121fcd
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/ResultWrapper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc;
+
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * @author kaizi2009
+ */
+public class ResultWrapper {
+
+ private Integer code;
+ private String message;
+
+ public ResultWrapper(Integer code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public void setCode(Integer code) {
+ this.code = code;
+ }
+
+ public static ResultWrapper error() {
+
+ return new ResultWrapper(-1, "System error");
+ }
+
+ public static ResultWrapper blocked() {
+ return new ResultWrapper(-2, "Blocked by Sentinel");
+ }
+
+ public String toJsonString() {
+ return JSONObject.toJSONString(this);
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java
new file mode 100644
index 00000000..f1ddb937
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+import java.util.Collections;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+/**
+ * @author kaizi2009
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class)
+@AutoConfigureMockMvc
+public class SentinelSpringMvcIntegrationTest {
+
+ private static final String HELLO_STR = "Hello!";
+ @Autowired
+ private MockMvc mvc;
+
+ @Test
+ public void testBase() throws Exception {
+ String url = "/hello";
+ this.mvc.perform(get(url))
+ .andExpect(status().isOk())
+ .andExpect(content().string(HELLO_STR));
+
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
+ assertNotNull(cn);
+ assertEquals(1, cn.passQps(), 0.01);
+ }
+
+ @Test
+ public void testOriginParser() throws Exception {
+ String springMvcPathVariableUrl = "/foo/{id}";
+ String limitOrigin = "userA";
+ final String headerName = "S-User";
+ configureRulesFor(springMvcPathVariableUrl, 0, limitOrigin);
+
+ // This will be passed since the caller is different: userB
+ this.mvc.perform(get("/foo/1").accept(MediaType.TEXT_PLAIN).header(headerName, "userB"))
+ .andExpect(status().isOk())
+ .andExpect(content().string("foo 1"));
+
+ // This will be blocked since the caller is same: userA
+ this.mvc.perform(
+ get("/foo/2").accept(MediaType.APPLICATION_JSON).header(headerName, limitOrigin))
+ .andExpect(status().isOk())
+ .andExpect(content().json(ResultWrapper.blocked().toJsonString()));
+
+ // This will be passed since the caller is different: ""
+ this.mvc.perform(get("/foo/3").accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(content().string("foo 3"));
+
+ FlowRuleManager.loadRules(null);
+ }
+
+ @Test
+ public void testTotalInterceptor() throws Exception {
+ String url = "/hello";
+ String totalTarget = "my_spring_mvc_total_url_request";
+ for (int i = 0; i < 3; i++) {
+ this.mvc.perform(get(url))
+ .andExpect(status().isOk())
+ .andExpect(content().string(HELLO_STR));
+ }
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode(totalTarget);
+ assertNotNull(cn);
+ assertEquals(3, cn.passQps(), 0.01);
+ }
+
+ @Test
+ public void testRuntimeException() throws Exception {
+ String url = "/runtimeException";
+ configureExceptionRulesFor(url, 3, null);
+ int repeat = 3;
+ for (int i = 0; i < repeat; i++) {
+ this.mvc.perform(get(url))
+ .andExpect(status().isOk())
+ .andExpect(content().string(ResultWrapper.error().toJsonString()));
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
+ assertNotNull(cn);
+ assertEquals(i + 1, cn.passQps(), 0.01);
+ }
+
+ // This will be blocked and response json.
+ this.mvc.perform(get(url))
+ .andExpect(status().isOk())
+ .andExpect(content().string(ResultWrapper.blocked().toJsonString()));
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode(url);
+ assertNotNull(cn);
+ assertEquals(repeat, cn.passQps(), 0.01);
+ assertEquals(1, cn.blockRequest(), 1);
+ }
+
+ private void configureRulesFor(String resource, int count, String limitApp) {
+ FlowRule rule = new FlowRule()
+ .setCount(count)
+ .setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule.setResource(resource);
+ if (StringUtil.isNotBlank(limitApp)) {
+ rule.setLimitApp(limitApp);
+ }
+ FlowRuleManager.loadRules(Collections.singletonList(rule));
+ }
+
+ private void configureExceptionRulesFor(String resource, int count, String limitApp) {
+ FlowRule rule = new FlowRule()
+ .setCount(count)
+ .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
+ rule.setResource(resource);
+ if (StringUtil.isNotBlank(limitApp)) {
+ rule.setLimitApp(limitApp);
+ }
+ FlowRuleManager.loadRules(Collections.singletonList(rule));
+ }
+
+ @After
+ public void cleanUp() {
+ FlowRuleManager.loadRules(null);
+ ClusterBuilderSlot.resetClusterNodes();
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptorTest.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptorTest.java
new file mode 100644
index 00000000..506d9b2d
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptorTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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 com.alibaba.csp.sentinel.adapter.spring.webmvc;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Eric Zhao
+ */
+public class SentinelWebInterceptorTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPassIllegalConfig() {
+ SentinelWebMvcConfig config = new SentinelWebMvcConfig();
+ config.setRequestAttributeName(null);
+ SentinelWebInterceptor interceptor = new SentinelWebInterceptor(config);
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/TestApplication.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/TestApplication.java
new file mode 100644
index 00000000..da9b6bba
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/TestApplication.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+/**
+ * @author kaizi2009
+ */
+@SpringBootApplication
+public class TestApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(TestApplication.class);
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java
new file mode 100644
index 00000000..b0c4112b
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * Config sentinel interceptor
+ *
+ * @author kaizi2009
+ */
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ //Add sentinel interceptor
+ addSpringMvcInterceptor(registry);
+
+ //If you want to sentinel the total flow, you can add total interceptor
+ addSpringMvcTotalInterceptor(registry);
+ }
+
+ private void addSpringMvcInterceptor(InterceptorRegistry registry) {
+ //Config
+ SentinelWebMvcConfig config = new SentinelWebMvcConfig();
+
+ config.setBlockExceptionHandler(new BlockExceptionHandler() {
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
+ String resourceName = e.getRule().getResource();
+ //Depending on your situation, you can choose to process or throw
+ if ("/hello".equals(resourceName)) {
+ //Do something ......
+ //Write string or json string;
+ response.getWriter().write("/Blocked by sentinel");
+ } else {
+ //Handle in global exception handling
+ throw e;
+ }
+ }
+ });
+
+ //Custom configuration if necessary
+ config.setHttpMethodSpecify(false);
+ config.setWebContextUnify(true);
+ config.setOriginParser(new RequestOriginParser() {
+ @Override
+ public String parseOrigin(HttpServletRequest request) {
+ return request.getHeader("S-user");
+ }
+ });
+
+ //Add sentinel interceptor
+ registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
+ }
+
+ private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
+ //Configure
+ SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
+
+ //Custom configuration if necessary
+ config.setRequestAttributeName("my_sentinel_spring_mvc_total_entity_container");
+ config.setTotalResourceName("my_spring_mvc_total_url_request");
+
+ //Add sentinel interceptor
+ registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelSpringMvcBlockHandlerConfig.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelSpringMvcBlockHandlerConfig.java
new file mode 100644
index 00000000..b8e46603
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelSpringMvcBlockHandlerConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.ResultWrapper;
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * Config 'BlockException' handler, handler it in spring veb 'ExceptionHandler'
+ *
+ * @author kaizi2009
+ */
+@ControllerAdvice
+@Order(0)
+public class SentinelSpringMvcBlockHandlerConfig {
+ private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @ExceptionHandler(BlockException.class)
+ @ResponseBody
+ public ResultWrapper sentinelBlockHandler(BlockException e) {
+ AbstractRule rule = e.getRule();
+ //Log
+ logger.info("Blocked by sentinel, {}", rule.toString());
+ //Return object
+ return ResultWrapper.blocked();
+ }
+
+ @ExceptionHandler(Exception.class)
+ @ResponseBody
+ public ResultWrapper exceptionHandler(Exception e) {
+ logger.error("System error", e.getMessage());
+ return new ResultWrapper(-1, "System error");
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/controller/TestController.java b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/controller/TestController.java
new file mode 100644
index 00000000..d8a42ab8
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-webmvc-6x-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/controller/TestController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * Licensed 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
+ *
+ * https://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 com.alibaba.csp.sentinel.adapter.spring.webmvc.controller;
+
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author kaizi2009
+ */
+@RestController
+public class TestController {
+
+ @GetMapping("/hello")
+ public String apiHello() {
+ return "Hello!";
+ }
+
+ @GetMapping("/err")
+ public String apiError() {
+ return "Oops...";
+ }
+
+ @GetMapping("/foo/{id}")
+ public String apiFoo(@PathVariable("id") Long id) {
+ return "foo " + id;
+ }
+
+ @GetMapping("/runtimeException")
+ public String runtimeException() {
+ int i = 1 / 0;
+ return "runtimeException";
+ }
+
+ @GetMapping("/exclude/{id}")
+ public String apiExclude(@PathVariable("id") Long id) {
+ return "Exclude " + id;
+ }
+
+}