Improve code and demo for sentinel-spring-webmvc-adapter module
Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
b14534fb35
commit
20ad3c8f79
|
|
@ -1,6 +1,8 @@
|
||||||
# Sentinel Spring MVC Interceptor
|
# Sentinel Spring MVC Adapter
|
||||||
|
|
||||||
Sentinel provides Spring MVC Interceptor integration to enable flow control for web requests, And support url like '/foo/{id}'
|
## Introduction
|
||||||
|
|
||||||
|
Sentinel provides integration for Spring Web to enable flow control for web requests.
|
||||||
|
|
||||||
Add the following dependency in `pom.xml` (if you are using Maven):
|
Add the following dependency in `pom.xml` (if you are using Maven):
|
||||||
|
|
||||||
|
|
@ -12,7 +14,7 @@ Add the following dependency in `pom.xml` (if you are using Maven):
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
Configure interceptor
|
Then we could add a configuration bean to configure the interceptor:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Configuration
|
@Configuration
|
||||||
|
|
@ -20,96 +22,85 @@ public class InterceptorConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
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) {
|
|
||||||
//Configure
|
|
||||||
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
||||||
//Custom configuration if necessary
|
// Enable the HTTP method prefix.
|
||||||
config.setHttpMethodSpecify(true);
|
config.setHttpMethodSpecify(true);
|
||||||
config.setOriginParser(request -> request.getHeader("S-user"));
|
// Add to the interceptor list.
|
||||||
//Add sentinel interceptor
|
registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
|
||||||
registry.addInterceptor(new SentinelInterceptor(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 SentinelTotalInterceptor(config)).addPathPatterns("/**");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Configure 'BlockException' handler, there are three options:
|
Then Sentinel will extract URL patterns defined in Web Controller as the web resource (e.g. `/foo/{id}`).
|
||||||
1. Global exception handling in spring MVC. <Recommend>
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Block handling
|
||||||
|
|
||||||
|
Sentinel Spring Web adapter provides a `BlockExceptionHandler` interface to handle the blocked requests.
|
||||||
|
We could set the handler via `SentinelWebMvcTotalConfig#setBlockExceptionHandler()` method.
|
||||||
|
|
||||||
|
By default the interceptor will throw out the `BlockException`.
|
||||||
|
We need to set a global exception handler function in Spring to handle it. An example:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
@Order(0)
|
@Order(0)
|
||||||
public class SentinelSpringMvcBlockHandlerConfig {
|
public class SentinelBlockExceptionHandlerConfig {
|
||||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@ExceptionHandler(BlockException.class)
|
@ExceptionHandler(BlockException.class)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public String sentinelBlockHandler(BlockException e) {
|
public String sentinelBlockHandler(BlockException e) {
|
||||||
AbstractRule rule = e.getRule();
|
AbstractRule rule = e.getRule();
|
||||||
logger.info("Blocked by sentinel, {}", rule.toString());
|
logger.info("Blocked by Sentinel: {}", rule.toString());
|
||||||
return "Blocked by Sentinel";
|
return "Blocked by Sentinel";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We've provided a `DefaultBlockExceptionHandler`. When a request is blocked, the handler will return a default page
|
||||||
|
indicating the request is rejected (`Blocked by Sentinel (flow limiting)`).
|
||||||
|
The HTTP status code of the default block page is **429 (Too Many Requests)**.
|
||||||
|
|
||||||
|
We could also implement our implementation of the `BlockExceptionHandler` interface and
|
||||||
|
set to the config object. An example:
|
||||||
|
|
||||||
```
|
|
||||||
2. Use `DefaultBlockExceptionHandler`
|
|
||||||
```java
|
```java
|
||||||
//SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
|
|
||||||
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
|
||||||
config.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
|
|
||||||
```
|
|
||||||
3. `implements BlockExceptionHandler`
|
|
||||||
```java
|
|
||||||
//SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
|
|
||||||
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
||||||
config.setBlockExceptionHandler((request, response, e) -> {
|
config.setBlockExceptionHandler((request, response, e) -> {
|
||||||
String resourceName = e.getRule().getResource();
|
String resourceName = e.getRule().getResource();
|
||||||
//Depending on your situation, you can choose to process or throw
|
// Depending on your situation, you can choose to process or throw
|
||||||
if ("/hello".equals(resourceName)) {
|
if ("/hello".equals(resourceName)) {
|
||||||
//Do something ......
|
// Do something ......
|
||||||
//Write string or error page;
|
response.getWriter().write("Blocked by Sentinel");
|
||||||
response.getWriter().write("Blocked by sentinel");
|
|
||||||
} else {
|
} else {
|
||||||
//Handle it in global exception handling
|
// Handle it in global exception handling
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Configuration
|
### Customized configuration
|
||||||
- Common configuration in `SentinelWebMvcConfig` and `SentinelWebMvcTotalConfig`
|
|
||||||
|
- Common configuration in `SentinelWebMvcConfig` and `SentinelWebMvcTotalConfig`:
|
||||||
|
|
||||||
| name | description | type | default value |
|
| name | description | type | default value |
|
||||||
|------|------------|------|-------|
|
|------|------------|------|-------|
|
||||||
| blockExceptionHandler| The handler when blocked by sentinel, there are three options:<br/>1. The default value is null, you can hanlde `BlockException` in spring MVC;<br/>2.Use `DefaultBlockExceptionHandler`;<br/>3. `implements BlockExceptionHandler` | `BlockExceptionHandler` | `null` |
|
| `blockExceptionHandler`| The handler that handles the block request | `BlockExceptionHandler` | null (throw out the BlockException) |
|
||||||
| originParser | `RequestOriginParser` interface is useful for extracting request origin (e.g. IP or appName from HTTP Header) from HTTP request | `RequestOriginParser` | `null` |
|
| `originParser` | Extracting request origin (e.g. IP or appName from HTTP Header) from HTTP request | `RequestOriginParser` | - |
|
||||||
|
|
||||||
- `SentinelWebMvcConfig` configuration
|
- `SentinelWebMvcConfig` configuration:
|
||||||
|
|
||||||
| name | description | type | default value |
|
| name | description | type | default value |
|
||||||
|------|------------|------|-------|
|
|------|------------|------|-------|
|
||||||
| urlCleaner | The `UrlCleaner` interface is designed for clean and unify the URL resource. For REST APIs, you can to clean the URL resource (e.g. `/api/user/getById` and `/api/user/getByName` -> `/api/user/getBy*`), avoid the amount of context and will exceed the threshold | `UrlCleaner` | `null` |
|
| urlCleaner | The `UrlCleaner` interface is designed for clean and unify the URL resource. | `UrlCleaner` | - |
|
||||||
| requestAttributeName | Attribute name in request used by sentinel, please check record log, if it is already used, please set | `String` | sentinel_spring_mvc_entry_container |
|
| requestAttributeName | Attribute key in request used by Sentinel (internal) | `String` | `$$sentinel_spring_web_entry_attr` |
|
||||||
| httpMethodSpecify | Specify http method, for example: GET:/hello | `boolean` | `false` |
|
| httpMethodSpecify | Specify whether the URL resource name should contain the HTTP method prefix (e.g. `POST:`). | `boolean` | `false` |
|
||||||
|
|
||||||
|
- `SentinelWebMvcTotalConfig` configuration:
|
||||||
`SentinelWebMvcTotalConfig` configuration
|
|
||||||
|
|
||||||
| name | description | type | default value |
|
| name | description | type | default value |
|
||||||
|------|------------|------|-------|
|
|------|------------|------|-------|
|
||||||
| totalResourceName | The resource name in `SentinelTotalInterceptor` | `String` | spring-mvc-total-url-request |
|
| totalResourceName | The resource name in `SentinelTotalInterceptor` | `String` | `spring-mvc-total-url-request` |
|
||||||
| requestAttributeName | Attribute name in request used by sentinel, please check record log, if it is already used, please set | `String` | sentinel_spring_mvc_total_entry_container |
|
| requestAttributeName | Attribute key in request used by Sentinel (internal) | `String` | `$$sentinel_spring_web_total_entry_attr` |
|
||||||
|
|
||||||
|
|
@ -3,13 +3,14 @@
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>sentinel-adapter</artifactId>
|
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<version>1.7.0-SNAPSHOT</version>
|
<artifactId>sentinel-adapter</artifactId>
|
||||||
|
<version>1.7.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
|
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring.version>5.1.8.RELEASE</spring.version>
|
<spring.version>5.1.8.RELEASE</spring.version>
|
||||||
|
|
|
||||||
|
|
@ -20,38 +20,47 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.Entry;
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
import com.alibaba.csp.sentinel.EntryType;
|
import com.alibaba.csp.sentinel.EntryType;
|
||||||
|
import com.alibaba.csp.sentinel.ResourceTypeConstants;
|
||||||
import com.alibaba.csp.sentinel.SphU;
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
import com.alibaba.csp.sentinel.Tracer;
|
import com.alibaba.csp.sentinel.Tracer;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.BaseWebMvcConfig;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.BaseWebMvcConfig;
|
||||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
|
* @since 1.7.1
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
|
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
public static final String SPRING_MVC_CONTEXT_NAME = "spring_mvc_context";
|
public static final String SENTINEL_SPRING_WEB_CONTEXT_NAME = "sentinel_spring_web_context";
|
||||||
private static final String EMPTY_ORIGIN = "";
|
private static final String EMPTY_ORIGIN = "";
|
||||||
protected static final String COLON = ":";
|
|
||||||
private BaseWebMvcConfig baseWebMvcConfig;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String resourceName = getResourceName(request);
|
String resourceName = getResourceName(request);
|
||||||
|
|
||||||
if (StringUtil.isNotEmpty(resourceName)) {
|
if (StringUtil.isNotEmpty(resourceName)) {
|
||||||
// Parse the request origin using registered origin parser.
|
// Parse the request origin using registered origin parser.
|
||||||
String origin = parseOrigin(request);
|
String origin = parseOrigin(request);
|
||||||
ContextUtil.enter(SPRING_MVC_CONTEXT_NAME, origin);
|
ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
|
||||||
Entry entry = SphU.entry(resourceName, EntryType.IN);
|
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
|
||||||
|
|
||||||
setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
|
setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
|
||||||
}
|
}
|
||||||
|
|
@ -63,9 +72,10 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get sentinel resource name.
|
* Return the resource name of the target web resource.
|
||||||
* @param request
|
*
|
||||||
* @return
|
* @param request web request
|
||||||
|
* @return the resource name of the target web resource.
|
||||||
*/
|
*/
|
||||||
protected abstract String getResourceName(HttpServletRequest request);
|
protected abstract String getResourceName(HttpServletRequest request);
|
||||||
|
|
||||||
|
|
@ -88,7 +98,8 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
|
||||||
protected void setEntryInRequest(HttpServletRequest request, String name, Entry entry) {
|
protected void setEntryInRequest(HttpServletRequest request, String name, Entry entry) {
|
||||||
Object attrVal = request.getAttribute(name);
|
Object attrVal = request.getAttribute(name);
|
||||||
if (attrVal != null) {
|
if (attrVal != null) {
|
||||||
RecordLog.warn(String.format("Already exist attribute name '%s' in request, please set `requestAttributeName`", name));
|
RecordLog.warn("[{}] The attribute key '{0}' already exists in request, please set `requestAttributeName`",
|
||||||
|
getClass().getSimpleName(), name);
|
||||||
} else {
|
} else {
|
||||||
request.setAttribute(name, entry);
|
request.setAttribute(name, entry);
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +107,7 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
|
||||||
|
|
||||||
protected Entry getEntryInRequest(HttpServletRequest request, String attrKey) {
|
protected Entry getEntryInRequest(HttpServletRequest request, String attrKey) {
|
||||||
Object entryObject = request.getAttribute(attrKey);
|
Object entryObject = request.getAttribute(attrKey);
|
||||||
return entryObject == null ? null : (Entry) entryObject;
|
return entryObject == null ? null : (Entry)entryObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removeEntryInRequest(HttpServletRequest request) {
|
protected void removeEntryInRequest(HttpServletRequest request) {
|
||||||
|
|
@ -112,11 +123,12 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
|
protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e)
|
||||||
|
throws Exception {
|
||||||
if (baseWebMvcConfig.getBlockExceptionHandler() != null) {
|
if (baseWebMvcConfig.getBlockExceptionHandler() != null) {
|
||||||
baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);
|
baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);
|
||||||
} else {
|
} else {
|
||||||
//Throw BlockException, handle it in spring mvc
|
// Throw BlockException directly. Users need to handle it in Spring global exception handler.
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +144,4 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
|
||||||
return origin;
|
return origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setBaseWebMvcConfig(BaseWebMvcConfig config) {
|
|
||||||
this.baseWebMvcConfig = config;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ 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.config.SentinelWebMvcConfig;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
@ -25,42 +24,32 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring mvc interceptor that integrates with sentinel.
|
* Spring Web MVC interceptor that integrates with Sentinel.
|
||||||
*
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
|
* @since 1.7.1
|
||||||
*/
|
*/
|
||||||
public class SentinelInterceptor extends AbstractSentinelInterceptor {
|
public class SentinelWebInterceptor extends AbstractSentinelInterceptor {
|
||||||
private SentinelWebMvcConfig config;
|
|
||||||
|
|
||||||
public SentinelInterceptor(SentinelWebMvcConfig config) {
|
private final SentinelWebMvcConfig config;
|
||||||
super();
|
|
||||||
setConfig(config);
|
|
||||||
super.setBaseWebMvcConfig(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SentinelInterceptor() {
|
public SentinelWebInterceptor() {
|
||||||
this(new SentinelWebMvcConfig());
|
this(new SentinelWebMvcConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SentinelInterceptor setConfig(SentinelWebMvcConfig config) {
|
public SentinelWebInterceptor(SentinelWebMvcConfig config) {
|
||||||
|
super(config);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
|
// Use the default config by default.
|
||||||
this.config = new SentinelWebMvcConfig();
|
this.config = new SentinelWebMvcConfig();
|
||||||
RecordLog.info("Config is null, use default config");
|
|
||||||
} else {
|
} else {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
RecordLog.info(String.format("SentinelInterceptor config: requestAttributeName=%s, originParser=%s, httpMethodSpecify=%s, blockExceptionHandler=%s, urlCleaner=%s", config.getRequestAttributeName(), config.getOriginParser(), config.isHttpMethodSpecify(), config.getBlockExceptionHandler(), config.getUrlCleaner()));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get target in HttpServletRequest
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected String getResourceName(HttpServletRequest request) {
|
protected String getResourceName(HttpServletRequest request) {
|
||||||
|
// Resolve the Spring Web URL pattern from the request attribute.
|
||||||
Object resourceNameObject = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
Object resourceNameObject = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||||
if (resourceNameObject == null || !(resourceNameObject instanceof String)) {
|
if (resourceNameObject == null || !(resourceNameObject instanceof String)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -72,7 +61,7 @@ public class SentinelInterceptor extends AbstractSentinelInterceptor {
|
||||||
}
|
}
|
||||||
// Add method specification if necessary
|
// Add method specification if necessary
|
||||||
if (StringUtil.isNotEmpty(resourceName) && config.isHttpMethodSpecify()) {
|
if (StringUtil.isNotEmpty(resourceName) && config.isHttpMethodSpecify()) {
|
||||||
resourceName = request.getMethod().toUpperCase() + COLON + resourceName;
|
resourceName = request.getMethod().toUpperCase() + ":" + resourceName;
|
||||||
}
|
}
|
||||||
return resourceName;
|
return resourceName;
|
||||||
}
|
}
|
||||||
|
|
@ -16,37 +16,31 @@
|
||||||
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
|
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring mvc interceptor for all requests.
|
* The web interceptor for all requests, which will unify all URL as
|
||||||
|
* a single resource name (configured in {@link SentinelWebMvcTotalConfig}).
|
||||||
*
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
|
* @since 1.7.1
|
||||||
*/
|
*/
|
||||||
public class SentinelTotalInterceptor extends AbstractSentinelInterceptor {
|
public class SentinelWebTotalInterceptor extends AbstractSentinelInterceptor {
|
||||||
private SentinelWebMvcTotalConfig config;
|
|
||||||
|
|
||||||
public SentinelTotalInterceptor(SentinelWebMvcTotalConfig config) {
|
private final SentinelWebMvcTotalConfig config;
|
||||||
super();
|
|
||||||
setConfig(config);
|
|
||||||
setBaseWebMvcConfig(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SentinelTotalInterceptor() {
|
public SentinelWebTotalInterceptor(SentinelWebMvcTotalConfig config) {
|
||||||
this(new SentinelWebMvcTotalConfig());
|
super(config);
|
||||||
}
|
|
||||||
|
|
||||||
public SentinelTotalInterceptor setConfig(SentinelWebMvcTotalConfig config) {
|
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
this.config = new SentinelWebMvcTotalConfig();
|
this.config = new SentinelWebMvcTotalConfig();
|
||||||
RecordLog.info("Config is null, use default config");
|
|
||||||
} else {
|
} else {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
RecordLog.info(String.format("SentinelInterceptor config: requestAttributeName=%s, originParser=%s, blockExceptionHandler=%s, totalResourceName=%s", config.getRequestAttributeName(), config.getOriginParser(), config.getBlockExceptionHandler(), config.getTotalResourceName()));
|
}
|
||||||
return this;
|
|
||||||
|
public SentinelWebTotalInterceptor() {
|
||||||
|
this(new SentinelWebMvcTotalConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -21,19 +21,19 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle BlockException
|
* Handler for the blocked request.
|
||||||
*
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
*/
|
*/
|
||||||
public interface BlockExceptionHandler {
|
public interface BlockExceptionHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle BlockException
|
* Handle the request when blocked.
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request Servlet request
|
||||||
* @param response
|
* @param response Servlet response
|
||||||
* @param e Depending on your situation, you can choose to process or throw BlockException
|
* @param e the block exception
|
||||||
* @throws Exception
|
* @throws Exception users may throw out the BlockException or other error occurs
|
||||||
*/
|
*/
|
||||||
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
|
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default `BlockException` handler
|
* Default handler for the blocked request.
|
||||||
*
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,6 +31,9 @@ public class DefaultBlockExceptionHandler implements BlockExceptionHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
|
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
|
||||||
|
// Return 429 (Too Many Requests) by default.
|
||||||
|
response.setStatus(429);
|
||||||
|
|
||||||
StringBuffer url = request.getRequestURL();
|
StringBuffer url = request.getRequestURL();
|
||||||
|
|
||||||
if ("GET".equals(request.getMethod()) && StringUtil.isNotBlank(request.getQueryString())) {
|
if ("GET".equals(request.getMethod()) && StringUtil.isNotBlank(request.getQueryString())) {
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,17 @@
|
||||||
package com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;
|
package com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean sentinel target
|
* Unify the resource target.
|
||||||
*
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
*/
|
*/
|
||||||
public interface UrlCleaner {
|
public interface UrlCleaner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean sentinel target
|
* Unify the resource target.
|
||||||
*
|
*
|
||||||
* @param originUrl
|
* @param originUrl the original URL
|
||||||
* @return
|
* @return the unified resource name
|
||||||
*/
|
*/
|
||||||
String clean(String originUrl);
|
String clean(String originUrl);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,16 @@ 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.BlockExceptionHandler;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common config
|
* Common base configuration for Spring Web MVC adapter.
|
||||||
*
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
|
* @since 1.7.1
|
||||||
*/
|
*/
|
||||||
public abstract class BaseWebMvcConfig {
|
public abstract class BaseWebMvcConfig {
|
||||||
|
|
||||||
protected String requestAttributeName;
|
protected String requestAttributeName;
|
||||||
protected BlockExceptionHandler blockExceptionHandler;
|
protected BlockExceptionHandler blockExceptionHandler;
|
||||||
protected RequestOriginParser originParser;
|
protected RequestOriginParser originParser;
|
||||||
|
|
@ -51,5 +54,4 @@ public abstract class BaseWebMvcConfig {
|
||||||
public void setOriginParser(RequestOriginParser originParser) {
|
public void setOriginParser(RequestOriginParser originParser) {
|
||||||
this.originParser = originParser;
|
this.originParser = originParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,20 @@ import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
|
* @since 1.7.1
|
||||||
*/
|
*/
|
||||||
public class SentinelWebMvcConfig extends BaseWebMvcConfig {
|
public class SentinelWebMvcConfig extends BaseWebMvcConfig {
|
||||||
|
|
||||||
public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "sentinel_spring_mvc_entry_container";
|
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;
|
private UrlCleaner urlCleaner;
|
||||||
protected boolean httpMethodSpecify;
|
/**
|
||||||
|
* Specify whether the URL resource name should contain the HTTP method prefix (e.g. {@code POST:}).
|
||||||
|
*/
|
||||||
|
private boolean httpMethodSpecify;
|
||||||
|
|
||||||
public SentinelWebMvcConfig() {
|
public SentinelWebMvcConfig() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -35,15 +43,28 @@ public class SentinelWebMvcConfig extends BaseWebMvcConfig {
|
||||||
return urlCleaner;
|
return urlCleaner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUrlCleaner(UrlCleaner urlCleaner) {
|
public SentinelWebMvcConfig setUrlCleaner(UrlCleaner urlCleaner) {
|
||||||
this.urlCleaner = urlCleaner;
|
this.urlCleaner = urlCleaner;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHttpMethodSpecify() {
|
public boolean isHttpMethodSpecify() {
|
||||||
return httpMethodSpecify;
|
return httpMethodSpecify;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHttpMethodSpecify(boolean httpMethodSpecify) {
|
public SentinelWebMvcConfig setHttpMethodSpecify(boolean httpMethodSpecify) {
|
||||||
this.httpMethodSpecify = httpMethodSpecify;
|
this.httpMethodSpecify = httpMethodSpecify;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SentinelWebMvcConfig{" +
|
||||||
|
"urlCleaner=" + urlCleaner +
|
||||||
|
", httpMethodSpecify=" + httpMethodSpecify +
|
||||||
|
", requestAttributeName='" + requestAttributeName + '\'' +
|
||||||
|
", blockExceptionHandler=" + blockExceptionHandler +
|
||||||
|
", originParser=" + originParser +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
|
package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
|
* @since 1.7.1
|
||||||
*/
|
*/
|
||||||
public class SentinelWebMvcTotalConfig extends BaseWebMvcConfig {
|
public class SentinelWebMvcTotalConfig extends BaseWebMvcConfig {
|
||||||
|
|
||||||
public static final String DEFAULT_TOTAL_RESOURCE_NAME = "spring-mvc-total-url-request";
|
public static final String DEFAULT_TOTAL_RESOURCE_NAME = "spring-mvc-total-url-request";
|
||||||
public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "sentinel_spring_mvc_total_entry_container";
|
public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "$$sentinel_spring_web_total_entry_attr";
|
||||||
|
|
||||||
private String totalResourceName = DEFAULT_TOTAL_RESOURCE_NAME;
|
private String totalResourceName = DEFAULT_TOTAL_RESOURCE_NAME;
|
||||||
|
|
||||||
|
|
@ -34,14 +35,18 @@ public class SentinelWebMvcTotalConfig extends BaseWebMvcConfig {
|
||||||
return totalResourceName;
|
return totalResourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public SentinelWebMvcTotalConfig setTotalResourceName(String totalResourceName) {
|
||||||
* Config total resource name
|
|
||||||
*
|
|
||||||
* @param totalResourceName
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public void setTotalResourceName(String totalResourceName) {
|
|
||||||
this.totalResourceName = totalResourceName;
|
this.totalResourceName = totalResourceName;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SentinelWebMvcTotalConfig{" +
|
||||||
|
"totalResourceName='" + totalResourceName + '\'' +
|
||||||
|
", requestAttributeName='" + requestAttributeName + '\'' +
|
||||||
|
", blockExceptionHandler=" + blockExceptionHandler +
|
||||||
|
", originParser=" + originParser +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
|
|
||||||
/*
|
/*
|
||||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||||
*
|
*
|
||||||
|
|
@ -14,6 +13,7 @@ package com.alibaba.csp.sentinel.adapter.spring.webmvc;
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
@ -46,7 +46,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest(classes = TestApplication.class)
|
@SpringBootTest(classes = TestApplication.class)
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
public class TestInterceptor {
|
public class SentinelSpringMvcIntegrationTest {
|
||||||
|
|
||||||
private static final String HELLO_STR = "Hello!";
|
private static final String HELLO_STR = "Hello!";
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
|
package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelInterceptor;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelTotalInterceptor;
|
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.BlockExceptionHandler;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
@ -74,7 +74,7 @@ public class InterceptorConfig implements WebMvcConfigurer {
|
||||||
});
|
});
|
||||||
|
|
||||||
//Add sentinel interceptor
|
//Add sentinel interceptor
|
||||||
registry.addInterceptor(new SentinelInterceptor(config)).addPathPatterns("/**");
|
registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
|
private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
|
||||||
|
|
@ -86,6 +86,6 @@ public class InterceptorConfig implements WebMvcConfigurer {
|
||||||
config.setTotalResourceName("my_spring_mvc_total_url_request");
|
config.setTotalResourceName("my_spring_mvc_total_url_request");
|
||||||
|
|
||||||
//Add sentinel interceptor
|
//Add sentinel interceptor
|
||||||
registry.addInterceptor(new SentinelTotalInterceptor(config)).addPathPatterns("/**");
|
registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,44 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>sentinel-demo</artifactId>
|
<artifactId>sentinel-demo</artifactId>
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<version>1.7.0-SNAPSHOT</version>
|
<version>1.7.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>sentinel-demo-spring-webmvc</artifactId>
|
<artifactId>sentinel-demo-spring-webmvc</artifactId>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
|
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<artifactId>sentinel-core</artifactId>
|
<artifactId>sentinel-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<artifactId>sentinel-transport-simple-http</artifactId>
|
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
|
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
<version>${spring.boot.version}</version>
|
<version>${spring.boot.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<version>${spring.boot.version}</version>
|
<version>${spring.boot.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -19,10 +19,10 @@ import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* <p>Add the JVM parameter to connect to the dashboard:</p>
|
||||||
|
* {@code -Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -Dproject.name=sentinel-demo-spring-webmvc}
|
||||||
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
* <code>
|
|
||||||
* -Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -Dproject.name=sentinel-demo-spring-webmvc
|
|
||||||
* </code>
|
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class WebMvcDemoApplication {
|
public class WebMvcDemoApplication {
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.demo.spring.webmvc.config;
|
package com.alibaba.csp.sentinel.demo.spring.webmvc.config;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelInterceptor;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelTotalInterceptor;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.DefaultBlockExceptionHandler;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
|
||||||
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;
|
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
@ -33,34 +35,28 @@ public class InterceptorConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
//Add sentinel interceptor
|
// Add Sentinel interceptor
|
||||||
addSpringMvcInterceptor(registry);
|
addSpringMvcInterceptor(registry);
|
||||||
|
|
||||||
//If you want to sentinel the total flow, you can add total interceptor
|
|
||||||
addSpringMvcTotalInterceptor(registry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSpringMvcInterceptor(InterceptorRegistry registry) {
|
private void addSpringMvcInterceptor(InterceptorRegistry registry) {
|
||||||
//Config
|
|
||||||
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
|
||||||
|
|
||||||
config.setBlockExceptionHandler((request, response, e) -> {
|
// Depending on your situation, you can choose to process the BlockException via
|
||||||
//Depending on your situation, you can choose to process or throw
|
// the BlockExceptionHandler or throw it directly, then handle it
|
||||||
boolean needThrow = true;
|
// in Spring web global exception handler.
|
||||||
if (needThrow) {
|
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
//Write string or json string;
|
|
||||||
response.getWriter().write("Blocked by sentinel");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Custom configuration if necessary
|
// config.setBlockExceptionHandler((request, response, e) -> { throw e; });
|
||||||
|
|
||||||
|
// Use the default handler.
|
||||||
|
config.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
|
||||||
|
|
||||||
|
// Custom configuration if necessary
|
||||||
config.setHttpMethodSpecify(true);
|
config.setHttpMethodSpecify(true);
|
||||||
config.setOriginParser(request -> request.getHeader("S-user"));
|
config.setOriginParser(request -> request.getHeader("S-user"));
|
||||||
|
|
||||||
//Add sentinel interceptor
|
// Add sentinel interceptor
|
||||||
registry.addInterceptor(new SentinelInterceptor(config)).addPathPatterns("/**");
|
registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
|
private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
|
||||||
|
|
@ -72,6 +68,6 @@ public class InterceptorConfig implements WebMvcConfigurer {
|
||||||
config.setTotalResourceName("my-spring-mvc-total-url-request");
|
config.setTotalResourceName("my-spring-mvc-total-url-request");
|
||||||
|
|
||||||
//Add sentinel interceptor
|
//Add sentinel interceptor
|
||||||
registry.addInterceptor(new SentinelTotalInterceptor(config)).addPathPatterns("/**");
|
registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
package com.alibaba.csp.sentinel.demo.spring.webmvc.config;
|
package com.alibaba.csp.sentinel.demo.spring.webmvc.config;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.demo.spring.webmvc.vo.ResultWrapper;
|
import com.alibaba.csp.sentinel.demo.spring.webmvc.vo.ResultWrapper;
|
||||||
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
@ -26,20 +25,23 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config blocked handler
|
* Spring configuration for global exception handler.
|
||||||
|
* This will be activated when the {@code BlockExceptionHandler}
|
||||||
|
* throws {@link BlockException directly}.
|
||||||
|
*
|
||||||
* @author kaizi2009
|
* @author kaizi2009
|
||||||
*/
|
*/
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
@Order(0)
|
@Order(0)
|
||||||
public class SentinelSpringMvcBlockHandlerConfig {
|
public class SentinelSpringMvcBlockHandlerConfig {
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@ExceptionHandler(BlockException.class)
|
@ExceptionHandler(BlockException.class)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResultWrapper sentinelBlockHandler(BlockException e) {
|
public ResultWrapper sentinelBlockHandler(BlockException e) {
|
||||||
AbstractRule rule = e.getRule();
|
logger.warn("Blocked by Sentinel: {}", e.getRule());
|
||||||
//Log
|
// Return the customized result.
|
||||||
logger.info("Blocked by sentinel, {}", rule.toString());
|
|
||||||
//Return object
|
|
||||||
return ResultWrapper.blocked();
|
return ResultWrapper.blocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue