137 lines
14 KiB
HTML
137 lines
14 KiB
HTML
<html><head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
<title>12. Developer Guide</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud-gateway.html" title="Spring Cloud Gateway"><link rel="up" href="multi_spring-cloud-gateway.html" title="Spring Cloud Gateway"><link rel="prev" href="multi__actuator_api.html" title="11. Actuator API"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">12. Developer Guide</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__actuator_api.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> </td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_developer_guide" href="#_developer_guide"></a>12. Developer Guide</h1></div></div></div><p>TODO: overview of writing custom integrations</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_route_predicate_factories" href="#_writing_custom_route_predicate_factories"></a>12.1 Writing Custom Route Predicate Factories</h2></div></div></div><p>TODO: document writing Custom Route Predicate Factories</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_gatewayfilter_factories" href="#_writing_custom_gatewayfilter_factories"></a>12.2 Writing Custom GatewayFilter Factories</h2></div></div></div><p>In order to write a GatewayFilter you will need to implement <code class="literal">GatewayFilterFactory</code>. There is an abstract class called <code class="literal">AbstractGatewayFilterFactory</code> which you can extend.</p><p><b>PreGatewayFilterFactory.java. </b>
|
|
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PreGatewayFilterFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PreGatewayFilterFactory() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(Config.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GatewayFilter apply(Config config) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// grab configuration from Config object</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//If you want to build a "pre" filter you need to manipulate the</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//request before calling chain.filter</span>
|
|
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//use builder to manipulate the request</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange.mutate().request(request).build());
|
|
};
|
|
}
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Put the configuration properties for your filter here</span>
|
|
}
|
|
|
|
}</pre><p>
|
|
</p><p><b>PostGatewayFilterFactory.java. </b>
|
|
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PostGatewayFilterFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PostGatewayFilterFactory() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(Config.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GatewayFilter apply(Config config) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// grab configuration from Config object</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -> {
|
|
ServerHttpResponse response = exchange.getResponse();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Manipulate the response in some way</span>
|
|
}));
|
|
};
|
|
}
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Put the configuration properties for your filter here</span>
|
|
}
|
|
|
|
}</pre><p>
|
|
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_global_filters" href="#_writing_custom_global_filters"></a>12.3 Writing Custom Global Filters</h2></div></div></div><p>In order to write a custom global filter, you will need to implement <code class="literal">GlobalFilter</code> interface. This will apply the filter to all requests.</p><p>Example of how to set up a Global Pre and Post filter, respectively</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter customGlobalFilter() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -> exchange.getPrincipal()
|
|
.map(Principal::getName)
|
|
.defaultIfEmpty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Default User"</span>)
|
|
.map(userName -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//adds header to proxied request</span>
|
|
exchange.getRequest().mutate().header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CUSTOM-REQUEST-HEADER"</span>, userName).build();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> exchange;
|
|
})
|
|
.flatMap(chain::filter);
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter customGlobalPostFilter() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -> chain.filter(exchange)
|
|
.then(Mono.just(exchange))
|
|
.map(serverWebExchange -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//adds header to response</span>
|
|
serverWebExchange.getResponse().getHeaders().set(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CUSTOM-RESPONSE-HEADER"</span>,
|
|
HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"It worked"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"It did not work"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> serverWebExchange;
|
|
})
|
|
.then();
|
|
}
|
|
---
|
|
|
|
=== Writing Custom Route Locators and Writers
|
|
|
|
TODO: document writing Custom Route Locators and Writers
|
|
|
|
== Building a Simple Gateway Using Spring MVC or Webflux
|
|
|
|
Spring Cloud Gateway provides a utility object called `ProxyExchange` which you can use inside a regular Spring web handler as a method parameter. It supports basic downstream HTTP exchanges via methods that mirror the HTTP verbs. With MVC it also supports forwarding to a local handler via the `forward()` method. To use the `ProxyExchange` just include the right module in your classpath (either `spring-cloud-gateway-mvc` or `spring-cloud-gateway-webflux`).
|
|
|
|
MVC example (proxying a request to <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/test"</span> downstream to a remote server):
|
|
|
|
```java
|
|
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> GatewaySampleApplication {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Value("${remote.home}")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> URI home;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@GetMapping("/test")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ResponseEntity<?> proxy(ProxyExchange<<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]> proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>).get();
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
The same thing with Webflux:
|
|
|
|
```java
|
|
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> GatewaySampleApplication {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Value("${remote.home}")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> URI home;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@GetMapping("/test")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Mono<ResponseEntity<?>> proxy(ProxyExchange<<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]> proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>).get();
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
There are convenience methods on the `ProxyExchange` to enable the handler method to discover and enhance the URI path of the incoming request. For example you might want to extract the trailing elements of a path to pass them downstream:
|
|
|
|
```java
|
|
<em><span class="hl-annotation" style="color: gray">@GetMapping("/proxy/path/**")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ResponseEntity<?> proxyPath(ProxyExchange<<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]> proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
String path = proxy.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/proxy/path/"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foos/"</span> + path).get();
|
|
}
|
|
```
|
|
|
|
All the features of Spring MVC or Webflux are available to Gateway handler methods. So you can inject request headers and query parameters, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> instance, and you can constrain the incoming requests with declarations in the mapping annotation. See the documentation <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> `<em><span class="hl-annotation" style="color: gray">@RequestMapping`</span></em> in Spring MVC <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> more details of those features.
|
|
|
|
Headers can be added to the downstream response using the `header()` methods on `ProxyExchange`.
|
|
|
|
You can also manipulate response headers (and anything <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> you like in the response) by adding a mapper to the `get()` etc. method. The mapper is a `Function` that takes the incoming `ResponseEntity` and converts it to an outgoing one.
|
|
|
|
First <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> support is provided <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sensitive"</span> headers (<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"cookie"</span> and <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization"</span> by <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>) which are not passed downstream, and <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"proxy"</span> headers (`x-forwarded-*`).</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__actuator_api.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> </td></tr><tr><td width="40%" align="left" valign="top">11. Actuator API </td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud-gateway.html">Home</a></td><td width="40%" align="right" valign="top"> </td></tr></table></div></body></html> |