Merge pull request #7898 from kamleshkr/BAEL-1363
BAEL-1363 Fallback for Zuul Routes
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
<module>spring-cloud-zookeeper</module>
|
||||
<module>spring-cloud-gateway</module>
|
||||
<module>spring-cloud-stream</module>
|
||||
<module>spring-cloud-stream-starters/twitterhdfs</module>
|
||||
<module>spring-cloud-stream-starters/twitterhdfs</module>
|
||||
<module>spring-cloud-connectors-heroku</module>
|
||||
<module>spring-cloud-aws</module>
|
||||
<module>spring-cloud-consul</module>
|
||||
@@ -35,9 +35,10 @@
|
||||
<module>spring-cloud-archaius</module>
|
||||
<module>spring-cloud-functions</module>
|
||||
<module>spring-cloud-vault</module>
|
||||
<!-- <module>spring-cloud-security</module> --> <!-- Fixing in BAEL-10887 -->
|
||||
<module>spring-cloud-task</module>
|
||||
<!-- <module>spring-cloud-security</module> --> <!-- Fixing in BAEL-10887 -->
|
||||
<module>spring-cloud-task</module>
|
||||
<module>spring-cloud-zuul</module>
|
||||
<module>spring-cloud-zuul-fallback</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
2
spring-cloud/spring-cloud-zuul-fallback/README.md
Normal file
2
spring-cloud/spring-cloud-zuul-fallback/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Relevant Articles:
|
||||
- [Fallback for Zuul Route](TODO)
|
||||
43
spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
Normal file
43
spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>api-gateway</artifactId>
|
||||
<name>api-gateway</name>
|
||||
<description>API Gateway using Zuul</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.spring.cloud</groupId>
|
||||
<artifactId>spring-cloud-zuul-fallback</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-parent</artifactId>
|
||||
<version>${spring-cloud-dependencies.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.baeldung.spring.cloud.apigateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableZuulProxy
|
||||
public class ApiGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ApiGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.baeldung.spring.cloud.apigateway.fallback;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
public class GatewayClientResponse implements ClientHttpResponse {
|
||||
|
||||
private HttpStatus status;
|
||||
private String message;
|
||||
|
||||
public GatewayClientResponse(HttpStatus status, String message) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return status.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return status.getReasonPhrase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return new ByteArrayInputStream(message.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return headers;
|
||||
}
|
||||
|
||||
public HttpStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(HttpStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.spring.cloud.apigateway.fallback;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixTimeoutException;
|
||||
|
||||
@Component
|
||||
class GatewayServiceFallback implements FallbackProvider {
|
||||
|
||||
private static final String DEFAULT_MESSAGE = "Service not available.";
|
||||
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "*"; // or return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
|
||||
if (cause instanceof HystrixTimeoutException) {
|
||||
return new GatewayClientResponse(HttpStatus.GATEWAY_TIMEOUT, DEFAULT_MESSAGE);
|
||||
} else {
|
||||
return new GatewayClientResponse(HttpStatus.INTERNAL_SERVER_ERROR, DEFAULT_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.spring.cloud.apigateway.fallback;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixTimeoutException;
|
||||
|
||||
@Component
|
||||
class WeatherServiceFallback implements FallbackProvider {
|
||||
|
||||
private static final String DEFAULT_MESSAGE = "Weather information is not available.";
|
||||
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "weather-service";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
|
||||
if (cause instanceof HystrixTimeoutException) {
|
||||
return new GatewayClientResponse(HttpStatus.GATEWAY_TIMEOUT, DEFAULT_MESSAGE);
|
||||
} else {
|
||||
return new GatewayClientResponse(HttpStatus.INTERNAL_SERVER_ERROR, DEFAULT_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
spring:
|
||||
application:
|
||||
name: api-gateway
|
||||
server:
|
||||
port: 7070
|
||||
|
||||
zuul:
|
||||
igoredServices: '*'
|
||||
routes:
|
||||
weather-service:
|
||||
path: /weather/**
|
||||
serviceId: weather-service
|
||||
strip-prefix: false
|
||||
|
||||
ribbon:
|
||||
eureka:
|
||||
enabled: false
|
||||
|
||||
weather-service:
|
||||
ribbon:
|
||||
listOfServers: localhost:8080
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.spring.cloud.apigateway;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class ApiGatewayApplicationIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.spring.cloud.apigateway.fallback;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixTimeoutException;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class GatewayServiceFallbackUnitTest {
|
||||
|
||||
private static final String ROUTE = "*";
|
||||
|
||||
@Autowired
|
||||
private GatewayServiceFallback fallback;
|
||||
|
||||
@Test
|
||||
public void testWhenGetRouteThenReturnWeatherServiceRoute() {
|
||||
assertEquals(ROUTE, fallback.getRoute());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackResponse_whenHystrixException_thenGatewayTimeout() throws Exception {
|
||||
HystrixTimeoutException exception = new HystrixTimeoutException();
|
||||
ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
|
||||
|
||||
assertEquals(HttpStatus.GATEWAY_TIMEOUT, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackResponse_whenNonHystrixException_thenInternalServerError() throws Exception {
|
||||
RuntimeException exception = new RuntimeException("Test exception");
|
||||
ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
|
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.spring.cloud.apigateway.fallback;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixTimeoutException;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class WeatherServiceFallbackUnitTest {
|
||||
|
||||
private static final String ROUTE = "weather-service";
|
||||
|
||||
@Autowired
|
||||
private WeatherServiceFallback fallback;
|
||||
|
||||
@Test
|
||||
public void testWhenGetRouteThenReturnWeatherServiceRoute() {
|
||||
assertEquals(ROUTE, fallback.getRoute());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackResponse_whenHystrixException_thenGatewayTimeout() throws Exception {
|
||||
HystrixTimeoutException exception = new HystrixTimeoutException();
|
||||
ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
|
||||
|
||||
assertEquals(HttpStatus.GATEWAY_TIMEOUT, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackResponse_whenNonHystrixException_thenInternalServerError() throws Exception {
|
||||
RuntimeException exception = new RuntimeException("Test exception");
|
||||
ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
|
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
|
||||
}
|
||||
|
||||
}
|
||||
28
spring-cloud/spring-cloud-zuul-fallback/pom.xml
Normal file
28
spring-cloud/spring-cloud-zuul-fallback/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-cloud-zuul-fallback</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-zuul-fallback</name>
|
||||
<description>Spring Cloud Zuul Fallback</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.spring.cloud</groupId>
|
||||
<artifactId>spring-cloud</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>api-gateway</module>
|
||||
<module>weather-service</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-dependencies.version>Finchley.SR2</spring-cloud-dependencies.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
</properties>
|
||||
</project>
|
||||
@@ -0,0 +1,39 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>weather-service</artifactId>
|
||||
<name>Weather Service</name>
|
||||
<description>Weather Service for Zuul Fallback Test</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung.spring.cloud</groupId>
|
||||
<artifactId>spring-cloud-zuul-fallback</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-parent</artifactId>
|
||||
<version>${spring-cloud-dependencies.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.spring.cloud.weatherservice;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/weather")
|
||||
public class WeatherController {
|
||||
|
||||
@GetMapping("/today")
|
||||
public String getMessage() {
|
||||
return "It's a bright sunny day today!";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.spring.cloud.weatherservice;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class WeatherServiceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WeatherServiceApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
spring:
|
||||
application:
|
||||
name: weather-service
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.spring.cloud.weatherservice;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebMvcTest(WeatherController.class)
|
||||
public class WeatherControllerIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void whenWeatherControllerInvoked_thenReturnWeatherInformation() throws Exception {
|
||||
this.mockMvc.perform(get("/weather/today"))
|
||||
.andDo(print())
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("bright sunny day")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.spring.cloud.weatherservice;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class WeatherServiceApplicationIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user