# gs-gateway **Repository Path**: bilibird/gs-gateway ## Basic Information - **Project Name**: gs-gateway - **Description**: spring-gateway官方示例代码 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-06-12 - **Last Updated**: 2024-08-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README :spring_version: current :spring_boot_version: 2.0.0.RELEASE :Controller: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/stereotype/Controller.html :DispatcherServlet: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html :SpringApplication: https://docs.spring.io/spring-boot/docs/{spring_boot_version}/api/org/springframework/boot/SpringApplication.html :ResponseBody: https://docs.spring.io/spring/docs/{spring_version}/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html :toc: :icons: font :source-highlighter: prettify :project_id: gs-gateway This guide walks you through how to use the Spring Cloud Gateway == What you'll build You'll build a gateway using https://cloud.spring.io/spring-cloud-gateway/[Spring Cloud Gateway]. == What you'll need :java_version: 1.8 include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/prereq_editor_jdk_buildtools.adoc[] include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/how_to_complete_this_guide.adoc[] include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/hide-show-gradle.adoc[] include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/hide-show-maven.adoc[] include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/hide-show-sts.adoc[] [[initial]] == Creating A Simple Route The Spring Cloud Gateway uses routes in order to process requests to downstream services. In this guide we will route all of our requests to https://httpbin.org[HTTPBin]. Routes can be configured a number of ways but for this guide we will use the Java API provided by the Gateway. To get started, create a new `Bean` of type `RouteLocator` in `Application.java`. `src/main/java/gateway/Application.java` [source,java,tabsize=2] ---- @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes().build(); } ---- The above `myRoutes` method takes in a `RouteLocatorBuilder` which can easily be used to create routes. In addition to just creating routes, `RouteLocatorBuilder` allows you to add predicates and filters to your routes so you can route handle based on certain conditions as well as alter the request/response as you see fit. Let's create a route that routes a request to `https://httpbin.org/get` when a request is made to the Gateway at `/get`. In our configuration of this route we will add a filter that will add the request header `Hello` with the value `World` to the request before it is routed. `src/main/java/gateway/Application.java` [source,java,tabsize=2] ---- @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes() .route(p -> p .path("/get") .filters(f -> f.addRequestHeader("Hello", "World")) .uri("http://httpbin.org:80")) .build(); } ---- To test our very simple Gateway, just run `Application.java`, it should be run on port `8080`. Once the application is running, make a request to `http://localhost:8080/get`. You can do this using cURL by issuing the following command in your terminal. [source,bash] ---- $ curl http://localhost:8080/get ---- You should receive a response back that looks like this [source,json] ---- { "args": {}, "headers": { "Accept": "*/*", "Connection": "close", "Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:56207\"", "Hello": "World", "Host": "httpbin.org", "User-Agent": "curl/7.54.0", "X-Forwarded-Host": "localhost:8080" }, "origin": "0:0:0:0:0:0:0:1, 73.68.251.70", "url": "http://localhost:8080/get" } ---- Notice that HTTPBin shows that the header `Hello` with the value `World` was sent in the request. == Using Hystrix Now lets do something a little more interesting. Since the services behind the Gateway could potentially behave poorly effecting our clients we might want to wrap the routes we create in circuit breakers. You can do this in the Spring Cloud Gateway using Hystrix. This is implemented via a simple filter that you can add to your requests. Lets create another route to demonstrate this. In this example we will leverage HTTPBin's delay API that waits a certain number of seconds before sending a response. Since this API could potentially take a long time to send its response we can wrap the route that uses this API in a `HystrixCommand`. Add a new route to our `RouteLocator` object that looks like the following `src/main/java/gateway/Application.java` [source,java,tabsize=2] ---- @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes() .route(p -> p .path("/get") .filters(f -> f.addRequestHeader("Hello", "World")) .uri("http://httpbin.org:80")) .route(p -> p .host("*.hystrix.com") .filters(f -> f.hystrix(config -> config.setName("mycmd"))) .uri("http://httpbin.org:80")). build(); } ---- There are some differences between this new route configuration and the previous one we created. For one, we are using the host predicate instead of the path predicate. This means that as long as the host is `hystrix.com` we will route the request to HTTPBin and wrap that request in a `HystrixCommand`. We do this by applying a filter to the route. The Hystrix filter can be configured using a configuration object. In this example we are just giving the `HystrixCommand` the name `mycmd`. Lets test this new route. Start the application, but this time we are going to make a request to `/delay/3`. It is also important that we include a `Host` header that has the a host of `hystrix.com` or else the request won't be routed. In cURL this would look like [source,bash] ---- $ curl --dump-header - --header 'Host: www.hystrix.com' http://localhost:8080/delay/3 ---- NOTE: We are using `--dump-header` to see the response headers, the `-` after `--dump-header` is telling cURL to print the headers to stdout. After executing this command you should see the following in your terminal [source,bash] ---- HTTP/1.1 504 Gateway Timeout content-length: 0 ---- As you can see Hystrix timed out waiting for the response from HTTPBin. When Hystrix times out we can optionaly provide a fallback so that clients do not just received a `504` but something more meaninful. In a production scenario you may return some data from a cache for example, but in our simple example we will just return a response with the body `fallback` instead. To do this, lets modify our Hystrix filter to provide a URL to call in the case of a timeout. `src/main/java/gateway/Application.java` [source,java,tabsize=2] ---- @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes() .route(p -> p .path("/get") .filters(f -> f.addRequestHeader("Hello", "World")) .uri("http://httpbin.org:80")) .route(p -> p .host("*.hystrix.com") .filters(f -> f.hystrix(config -> config .setName("mycmd") .setFallbackUri("forward:/fallback"))) .uri("http://httpbin.org:80")) .build(); } ---- Now when the Hystrix wrapped route times out it will call `/fallback` in the Gateway app. Lets add the `/fallback` endpoint to our application. In `Application.java` add the class level annotation `@RestController`, then add the following `@RequestMapping` to the class. `src/main/java/gateway/Application.java` [source,java,tabsize=2,indent=0] ---- include::complete/src/main/java/gateway/Application.java[tag=fallback] ---- To test this new fallback functionality, restart the application and again issue the following cURL command [source,bash] ---- $ curl --dump-header - --header 'Host: www.hystrix.com' http://localhost:8080/delay/3 ---- With the fallback in place, we now see that we get a `200` back from the Gateway with the response body of `fallback`. [source,bash] ---- HTTP/1.1 200 OK transfer-encoding: chunked Content-Type: text/plain;charset=UTF-8 fallback ---- == Writing Tests As a good developer, we should write some tests to make sure our Gateway is doing what we expect it should. In most cases we want to limit out dependencies on outside resources, especially in unit tests, so we should not depend on HTTPBin. One solution to this problem is to make the URI in our routes configurable, so we can easily change the URI if we need to. In `Application.java` create a new class called `UriConfiguration`. [source,java,tabsize=2] ---- include::complete/src/main/java/gateway/Application.java[tag=uri-configuration] ---- To enable this `ConfigurationProperties` we need to also add a class-level annotation to `Application.java`. ``` @EnableConfigurationProperties(UriConfiguration.class) ``` With our new configuration class in place lets use it in the `myRoutes` method. `src/main/java/gateway/Application.java` [source,java,tabsize=2,indent=0] ---- include::complete/src/main/java/gateway/Application.java[tag=route-locator] ---- As you can see, instead of hardcoding the URL to HTTPBin we are getting the URL from our new configuration class instead. Below is the complete contents of `Application.java`. `src/main/java/gateway/Application.java` [source,java,tabsize=2] ---- include::complete/src/main/java/gateway/Application.java[tag=code] ---- Create a new class called `ApplicationTest` in `src/main/test/java/gateway`. In the new class add the following content. [source,java,tabsize=2] ---- include::complete/src/test/java/gateway/ApplicationTest.java[tag=code] ---- Our test is actually taking advantage of WireMock from Spring Cloud Contract in order stand up a server that can mock the APIs from HTTPBin. The first thing to notice is the use of `@AutoConfigureWireMock(port = 0)`. This annotation will start WireMock on a random port for us. Next notice that we are taking advantage of our `UriConfiguration` class and setting the `httpbin` property in the `@SpringBootTest` annotation to the WireMock server running locally. Within the test we then setup "stubs" for the HTTPBin APIs we call via the Gateway and mock the behavior we expect. Finally we use `WebTestClient` to actually make requests to the Gateway and validate the responses. == Summary Congratulations! You've just built your first Spring Coud Gateway application! include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/main/footer.adoc[]