There are 2 ways to add request and response headers via a bean from RouteLocator or via properties.
However would need an example to add default filters (below) via a bean as the service-response-value and service-request-value would be a dynamic value :
spring
cloud:
gateway:
default-filters:
- AddRequestHeader= service-request, service-request-value
- AddResponseHeader= service-response, service-response-value
Spring Cloud Gateway doesn't support default filters via Beans
https://github.com/spring-cloud/spring-cloud-gateway/issues/263
Related
I am building a REST application with Spring Boot and Webflux. There will be number of different endpoints that are will return a JSON resources.
Also, there is a requirement that the endpoints should be capable of returning different responses when:
Accept: application/json then JSON response
Accept: text/html then simple HTML page, where response JSON is formatted and simple CSS applied.
As there can be 100+ endpoints then it would be nice if this could be done dynamically instead of creating two #RequestMapping methods for each media type.
How could this be solved using Spring Boot and Webflux?
I have searched Google and have few ideas of using ByteBuddy to dynamically create the text/html endpoints.
If just adding produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_HTML_VALUE} then I get error in logs:
no encoder for with preset Content-Type 'null'
You could use Spring WebFlux Functional Endpoints that "a lightweight functional programming model in which functions are used to route and handle requests" instead of #RequestMapping. It provides more flexible model and you can generate routes dynamically and reuse handlers.
#Bean
public RouterFunction<ServerResponse> setupRoutes() {
String uri = "/test";
return
route(
POST(uri).and(accept(MediaType.TEXT_HTML)),
routeHandler::getHtml
)
.andRoute(
POST(uri).and(accept(MediaType.APPLICATION_JSON)),
routeHandler::getJson
);
}
I'm using Spring Cloud API Gateway, the app has yaml based configuration and it works well since all routes are internal links, means there is no need for any proxy.
But, there a dependency on Spring Security which is used to validate JWT token and the URL of issuer is the resource that can be reached only via proxy.
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: https://some-site.com/itmp/v1/.well-known/jwks.json
issuer-uri: https://some-site.com/itmp/v1/
And here is the problem: I can not force WebClient using the http.proxyHost parameter. I tried many techniques that I found all around the Internet like setting them implicitly in app properties file, or hard coded in application itself. But nothing worked out for me. Maybe I'm missing something, maybe Spring Cloud has some special tweaks for that behavior?
FYI Project is based on Spring Boot 2.6.6 (reactor-netty 1.0.17 which is normally supports proxyWithSystemProperties )
I recently had this exact problem myself. After a lot of back and forth I ended up creating my own ReactiveJwtDecoder bean and setting the HttpClient/WebClient it uses internally (using NimbusReactiveJwtDecoder.webClient(WebClient)). Something along the lines of:
#Bean
ReactiveJwtDecoder jwtDecoder(#Value("${ISSUER_URI}") String issuerUri,
#Value("${JWKS_URI}") String jwksUri) {
HttpClient httpClient = HttpClient.create().proxyWithSystemProperties();
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
var jwtDecoder = NimbusReactiveJwtDecoder
.withJwkSetUri(jwksUri)
.webClient(webClient)
.build();
// import org.springframework.security.oauth2.jwt.JwtValidators;
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(issuerUri));
return jwtDecoder;
}
You can probably inject the OAuth2ResourceServerProperties-bean instead of those two #Value-annotated Strings to get the URLs.
Also, be aware that if you application already does more customized JWT claim validation than merely checking iss, exp and nbf, you must remember to inject the existing JwtValidator and use that instead of the JwtValidators.createDefaultWithIssuer(String) one.
I have trouble in applying Java Spring boots with some rules:
- If parameters values do not match the specifics possible values you should return HTTP status code 400("Bad Request")
- Responses should have Content-Type header with approriate value(application/json)
- If you need to create multiple classes
- You can use only the following libraries:
Spring web MVC (v.5.0.7.Release)
Faster XML Jackson, Jackson datatype JSR310
I need detailed method read healtcheck with message:
{"currentTime" : "2020-06-01T20:45:35Z,"application":"OK"}
You can customise the health check endpoint by implementing HealthIndicator and providing custom logic.
For example, you can check out simple implementation here (https://www.amitph.com/custom-health-check-spring-boot-actuator/).
We are having three modules Gateway using spring-cloud-gateway, Service1 and Service2.
Gateway call service1 and service1 calls service2. Service1 and Service2 are using spring web flux
Gateway have 3 filters. global filter, Pre filter and post filter.
spring:
cloud:
gateway:
default-filters:
- name: GlobalFilter
routes:
id: service1
uri: http://localhost:9091/
predicates:
- Path=/service1/**
filters:
- name: PreFilter
- name: PostFilter
In global filter we are reading value from request body setting set as new baggage key.
ExtraFieldPropagation.set(tracer.currentSpan().context() ,X-CUST_TRAN_ID, transactionID);
We have also set Slueth properties like below so that it will be printed in logs as well as forwarded to next services (service1 and service2)
sleuth:
baggage-keys:
- X-CUST_TRAN_ID
log:
slf4j:
whitelisted-mdc-keys:
- X-CUST_TRAN_ID
Problem we are facing is
X-CUST_TRAN_ID values gets send via request and response and printed in service1 and service2's logs but not in Gateway itself.
It does not gets printed either in GlobalFilter's log or in PreFilter log statements but gets printed in PostFilters log statements. Since CUST_TRAN_ID is set in GlobalFilter's filter it should get printed in log statements after set to context.
Please help is there in better way to set the field for propagation as well as printing in log statements.
Sleuth initializes it's context using it's own filter which have a default order. you can try to give an highest precedence of the sleuth filter using the property:
spring:
sleuth:
web:
filter-order: -2147483648
In spring integration you can set the properties mapped-request-headers, mapped-response-headers and header-mapper in an outbound-gateway.
My target is to filter out specific headers, so I need some kind of blacklist. But the first two attributes are whitelists. I can either say mapped-request-headers="*", then every header will be passed, or I can put some specific headers, but then only these headers will be passed. But that's not what I want.
I could somehow overwrite the header mapper and add this bean to the header-mapper attribute, but is this the way of doing this? (I wonder why headers can be whitelisted but not blacklisted). Isn't there some functionality to set "filter spring added headers" and "pass manually added headers" or something?
You don't say what Spring Integration version you are using, or the type of endpoint you are using.
Since version 4.3, mappers that extend from AbstractHeaderMapper (AMQP, SOAP, XMPP) now support negated headers "!foo,!bar,baz*".
If you are not using one of these protocols, you can use a header filter upstream of the endpoint (for outbound) or right after the endpoint (inbound).
For the HTTP header mapper (DefaultHttpHeaderMapper) you can wire in a custom bean of that type where you add any standard headers you wish to exclude via the excludedOutboundStandardRequestHeaderNames and excludedInboundStandardResponseHeaderNames properties.