Modify response body in Spring Cloud gateway Post Filter - spring-boot

I want to fetch the response received from microservice in gateway and add that response to a new ConsumerResponse Object and set that as the new response to my UI in Post Filter of Gateway.
Say my response from microservice is a ResponseEntity of Object, Json Object like below,
{
"userDetails": {
"userId": 24,
"userName": "ABC",
"description": "ABC from ZZZ",
"registrationStatus": "REGISTERED",
"registrationTime": [
2022,
5,
23,
21,
17,
41,
465000000
],
"lastUpdatedTime": [
2022,
5,
23,
21,
17,
41,
465000000
]
}
}
Below is my ConsumerResponse Class, in which i want to set the above Json data in T data,
#JsonInclude(JsonInclude.Include.NON_NULL)
#Data
public class ConsumerResponse implements Serializable {
private static final long serialVersionUID = 1L;
private ConsumerResponse() {}
public static <T> ResponseEntity<ResponseEnvelope<Object>> ok(
T data, String apiVersion, Status status, HttpStatus httpStatus) {
return ResponseEntity.status(httpStatus)
.body(ResponseEnvelope.builder().apiVersion(apiVersion).data(data).status(status).build());
}
}
Below is my Gateway Filter class,
Took reference from this link How to get Original response body in Spring cloud gateway (Webflux) Post filter
In the above link, its creating a new String response body, but i want the response received from the microservice.
package org.xyz.gateway.filter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#Component
public class AuthFilter implements GlobalFilter {
private final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
#Autowired
private IdService idService;
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// code to fetch id from request
MyResult response = idService.getEntitlements(id);
ArrayList<String> rolesList = response.getRoles()
.values()
.stream()
.distinct()
.collect(Collectors.toCollection(ArrayList::new));
ServerHttpRequest httpRequest = exchange.getRequest()
.mutate()
.header("id", id)
.header("roles", rolesList.toString())
.build();
ServerWebExchange mutatedExchange = exchange
.mutate()
.request(httpRequest)
.build();
return chain.filter(mutatedExchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse serverHttpResponse = exchange.getResponse();
HttpStatus responseStatus = serverHttpResponse.getStatusCode();
DataBuffer dataBuffer = null;
String path = exchange.getRequest().getPath().toString();
ServerHttpRequest serverHttpRequest = exchange.getRequest();
DataBufferFactory dataBufferFactory = serverHttpResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = getDecoratedResponse(path, serverHttpResponse, serverHttpRequest, dataBufferFactory);
try {
dataBuffer = dataBufferFactory.wrap(
new ObjectMapper().writeValueAsBytes(
// need to set the response data here in ConsumerResponse Object below
ConsumerResponse.ok(decoratedResponse.toString(), GatewayConstants.VERSION, Status.OK, responseStatus)));
} catch (JsonProcessingException e) {
dataBuffer = serverHttpResponse.bufferFactory().wrap("".getBytes());
}
logger.info("decoratedResponse = {}", decoratedResponse);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
serverHttpResponse.getHeaders().setContentLength(decoratedResponse.toString().length());
serverHttpResponse.writeWith(Mono.just(dataBuffer)).subscribe();
exchange.mutate().response(serverHttpResponse).build();
}));
}
private ServerHttpResponseDecorator getDecoratedResponse(String path, ServerHttpResponse response, ServerHttpRequest request, DataBufferFactory dataBufferFactory) {
return new ServerHttpResponseDecorator(response) {
#Override
public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
logger.info("writeWith entering(...)---> with body = {} ", body);
if (body instanceof Flux) {
logger.info("Body is Flux");
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
DefaultDataBuffer joinedBuffers = new DefaultDataBufferFactory().join(dataBuffers);
byte[] content = new byte[joinedBuffers.readableByteCount()];
joinedBuffers.read(content);
String responseBody = new String(content, StandardCharsets.UTF_8);//MODIFY RESPONSE and Return the Modified response
logger.debug("requestId: {}, method: {}, url: {}, \nresponse body :{}", request.getId(), request.getMethodValue(), request.getURI(), responseBody);
return dataBufferFactory.wrap(responseBody.getBytes());
})).onErrorResume(err -> {
logger.error("error while decorating Response: {}",err.getMessage());
return Mono.empty();
});
}
return super.writeWith(body);
}
};
}
}
Any help would be greatly appreciated.
Thanks in advance.

Related

How I can call method using web client?

I wanna show new scene (JavaFX) when my post-request will be successfully completed, and show alerts when it will be 5xx/4xx errors.
When i try to do this using WebFlux library, I getting error like "Bad return type in lambda expression: Optional cannot be converted to Mono<? extends Throwable>"
AuthController.java
buttonAuth.setOnAction(
actionEvent -> {
UserAuth userAuth = UserAuth
.builder()
.login(loginAuth.getText())
.password(passAuth.getText())
.build();
webClient.post()
.uri("http://localhost:10120/message/authorization")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(userAuth)
.retrieve()
.onStatus(
HttpStatus::is5xxServerError,
response -> alertService.showAlert(Alert.AlertType.ERROR, "Error",
"Server error", false))
.onStatus(
HttpStatus::is4xxClientError,
response -> alertService.showAlert(Alert.AlertType.ERROR, "Error",
"Client error", false))
.onStatus(
HttpStatus.OK,
response -> Platform.runLater(()-> {setScene(buttonAuth,"Main Menu", MainMenuController.class, fxWeaver);}))
.bodyToMono(Void.class)
.block();
}
});
UserAuth.java
import lombok.*;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#ToString
#Builder(toBuilder = true)
public class UserAuth {
public String login;
public String password;
}
AlertService.java
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
#Service
#RequiredArgsConstructor
public class AlertService {
public Optional<ButtonType> showAlert(Alert.AlertType type, String title, String content, boolean NeedToReturnResult) {
Alert alert = new Alert(type);
alert.setTitle(title);
alert.setContentText(content);
alert.setHeaderText(null);
Optional<ButtonType> result = alert.showAndWait();
if (NeedToReturnResult) return result;
else {
return null;
}
}
}
BaseController.java --- need to set scene when request successfully completed
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import net.rgielen.fxweaver.core.FxWeaver;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class BaseController {
#Autowired
private Stage stage;
public void setScene(Button button, String title, Class controller, FxWeaver fxWeaver) {
button.getScene().getWindow().hide();
Parent root = (Parent) fxWeaver.loadView(controller);
stage.setScene(new Scene(root));
stage.setTitle(title);
stage.setResizable(false);
stage.show();
}
public void setScene(Hyperlink link, String title, Class controller, FxWeaver fxWeaver) {
link.getScene().getWindow().hide();
Parent root = (Parent) fxWeaver.loadView(controller);
stage.setTitle(title);
stage.setResizable(false);
stage.setScene(new Scene(root));
stage.show();
}
public void clearFields(TextField firstNameReg, TextField secondNameReg, TextField loginReg, PasswordField passReg, TextField mailReg) {
firstNameReg.clear();
secondNameReg.clear();
loginReg.clear();
passReg.clear();
mailReg.clear();
}
public void clearFields(TextField login, PasswordField password) {
login.clear();
password.clear();
}
}
Server-part -- here I tried to send some statuses when get request, but i don't know how to do that right
#RequestMapping(
value = "/authorization",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<String> authorizationUser(#Valid UserDTO userDTO) {
log.info("[UserController.authorizationUser] personDTO = {}", userDTO);
log.info("[UserController.authorizationUser] personDTO = {}", userService.authorizationUser(userDTO).authorization);
if(userService.authorizationUser(userDTO).authorization){
return Flux.just("ok");
}else{
return Flux.just("bad_request");
}
}

Spring webclient testing with okhttp3 Mockwebserver

I am unable to moock webclient
WebClientConfig.java
public #Bean("oauthWebClient") WebClient oauthWebClient() {
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
.doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10)))))
.filter(logOAuthResponse()).baseUrl(oauthTokenUrl)
.defaultHeaders(headers -> {
headers.setBasicAuth(secretHeaderValue);
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
})
.defaultUriVariables(oauthUriVariables)
.build();
}
serviceUti.java
package com.test.stats.volts.busops.utils;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.test.stats.volts.busops.exception.VoltsDataAccessException;
import com.test.stats.volts.busops.exception.MtnDataAccessNotRespondingException;
import com.test.stats.volts.busops.exception.VoltsDataAcessException;
import com.test.stats.volts.busops.exception.UnableToGetOAuthTokenException;
#Component("voltsDataAccessServiceUtil")
public class ServiceUtil {
private static final Logger logger = LoggerFactory.getLogger(ServiceUtil.class);
#Autowired
#Qualifier("voltsDataAccessWebClient")
WebClient voltsDataAccessClient;
#Autowired
#Qualifier("oauthWebClient")
WebClient oauthClient;
#Value("${volts.dataaccess.end.point.save-meeting}")
private String saveMeetingEndPoint;
#Value("${volts.dataaccess.oauth.header-key.name}")
private String oauthHeaderKeyName;
#Value("${volts.dataccess.oauth.response.token.key-name}")
private String oauthResponseTokenKeyName;
#Value("#{${volts.dataaccess.api.endpoints.default.uri-variables}}")
private Map<String, String> voltsDataAccessUriVariables;
#Autowired
ObjectMapper mapper;
#Value("#{${volts.dataaccess.oauth.uri-variables}}")
private Map<String, String> oauthUriVariables;
#Value("${volts.dataaccess.oauth.scope}")
private String oauthScope;
#Value("${volts.dataaccess.oauth.grant_type}")
private String grantType;
#Value("${volts.dataaccess.oauth.secret.header-name}")
private String secretHeaderName;
#Value("${volts.dataaccess.oauth.secret.header-value}")
private String secretHeaderValue;
#Value("${volts.dataaccess.end.point.fetch-meeting}")
private String fetchMeetingEndPoint;
#Value("${volts.dataaccess.query.param.api-key-value}")
private String apiKey;
#Retryable(maxAttempts = 2)
public String postOAuthToken() throws VoltsDataAccessException {
try {
String respEntity = oauthClient.post()
.uri(uriBuilder -> uriBuilder.queryParam("scope", oauthScope).queryParam("grant_type", grantType)
.build())
.headers(headers -> headers.setBasicAuth(secretHeaderValue))
.retrieve()
.bodyToMono(String.class)
.block();
if (respEntity == null)
throw new UnableToGetOAuthTokenException(
"Exception while fetching OAUthToken as response from OAUth service is null resposne body");
try {
return mapper.readValue(respEntity, ObjectNode.class).get(oauthResponseTokenKeyName).asText();
} catch (JsonProcessingException e) {
logger.error("Exception while pasring token from Oauth ", e);
throw new UnableToGetOAuthTokenException(
"Exception while fetching OAUthToken as response from OAUth service is null resposne body");
}
} catch (WebClientRequestException e) {
logger.error("Request Exception to OAUth: message {}", e.getMessage(), e);
logger.error("", e);
throw new VoltsDataAccessException(e.getMessage(), HttpStatus.REQUEST_TIMEOUT.value());
}
catch (WebClientResponseException e) {
throw new VoltsDataAccessException(e.getMessage(), e.getRawStatusCode());
}
}
#Retryable(value = VoltsDataAcessException.class)
public ResponseEntity<VoltsResp> postSaveMeetingDetails(SaveVoltsReq voltsReq)
throws VoltsDataAcessException {
try {
ResponseEntity<VoltsResp> resp = voltsDataAccessClient.post()
.uri(saveMeetingEndPoint, uri -> uri.queryParam("apikey", apiKey).build())
.headers(headers -> headers.setBearerAuth(postOAuthToken()))
.contentType(MediaType.APPLICATION_JSON).bodyValue(voltsReq).retrieve()
.toEntity(VoltsResp.class).block();
return resp;
} catch (WebClientRequestException e) {
throw new VoltsDataAcessException(e.getMessage(), HttpStatus.REQUEST_TIMEOUT.value());
} catch (WebClientResponseException e) {
throw new VoltsDataAcessException(e.getMessage(), e.getRawStatusCode());
}
}
}
My test classs
WebClienrTests.java
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.reactive.function.client.WebClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.stats.volts.busops.config.WebClientConfig;
import com.test.stats.volts.busops.utils.ServiceUtil;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
#SpringBootTest(classes = { ServiceUtil.class, WebClientConfig.class,
ObjectMapper.class })
#RunWith(SpringRunner.class)
class ServiceUtilTests {
public static MockWebServer mockServer = new MockWebServer();
#MockBean
WebClientConfig webClientCondfig;
#Autowired
ServiceUtil serviceUtil;
#MockBean(name = "oauthWebClient")
WebClient oauthClient;
#MockBean(name = "voltsDataAccessWebClient")
WebClient voltsDataAccessWebClient;
#BeforeEach
void startMockServer() throws Exception {
mockServer.start(9090);
oauthClient = WebClient.create(mockServer.url("/").url().toString());
}
#AfterEach
void stopMockServer() throws Exception {
mockServer.shutdown();
}
#Test
void testPostOAuth() throws Exception {
mockServer.enqueue(new MockResponse().setResponseCode(200).setBody("OAuthToken"));
String expected = serviceUtil.postOAuthToken();
assertThat(expected).isNotNull().isEqualTo("OAuthToken");
}
}
When I run above test I get this error
java.lang.NullPointerException:
At postAuthToken() while setting headers .headers(headers -> headers.setBasicAuth(secretHeaderValue))
Please help on how to mock whole thing

How to get BearerTokenAuthentication from SecurityContext in a GlobalFilter in Spring Cloud Gateway

Spring Cloud Gateway as a OAuth2ResourceServer with following Authorisation Config:
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchanges ->
exchanges
.anyExchange().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerSpec::jwt)
return http.build();
}
I have a global filter in place which is responsible for performing some functions at each valid authenticated request, something like this:
#Service
public class CustomGlobal implements GlobalFilter {
#Autowired
BearerTokenAuthentication authentication;
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// access request headers, and perform some logic
// extract details from the JWT token, and perform some logic
log.info(authentication.getTokenAttributes.get("sub"));
// ^ in the above line there's a NullPointerException, since instance
// BearerTokenAuthentication is not set, or not visible at a GlobalFilter class
return chain.filter(exchange);
}
}
I am still in a learning phase. Any possible leads would be appreciated.
I did that this way(Note you should change WebFilter to GlobalFilter).
Add into your pom
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
<version>5.4.6</version>
</dependency>
Then Filter should be like
package filter;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
#Log4j2
public class CustomGlobal implements WebFilter {
public static final String HEADER_PREFIX = "Bearer ";
private final ReactiveJwtDecoder jwtDecoder;
public ReactiveJwtDecoder createDecoder(String issuer, String jwkUrl) {
var jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkUrl).build();
jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
new JwtIssuerValidator(issuer),
new JwtTimestampValidator()));
return jwtDecoder;
}
protected CustomGlobal(String issuer, String jwkUrl) {
this.jwtDecoder = createDecoder(issuer, jwkUrl);
}
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return Mono
.defer(() -> {
var token = resolveToken(exchange.getRequest());
if (!StringUtils.hasText(token)) {
throw new BadJwtException("Authorisation token is invalid");
}
return jwtDecoder.decode(token);
})
.flatMap(tokenJwt -> {
log.info(tokenJwt.getClaimAsString("sub"));
return chain.filter(exchange);
})
.onErrorResume(err -> handleError(exchange));
}
private Mono<Void> handleError(ServerWebExchange exchange) {
exchange.getResponse().setRawStatusCode(HttpStatus.UNAUTHORIZED.value());
exchange.getResponse().getHeaders().add("Content-Type", "application/json");
return exchange.getResponse().setComplete();
}
private String resolveToken(ServerHttpRequest request) {
String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(HEADER_PREFIX)) {
return bearerToken.substring(7).trim();
}
return "";
}
}
Next step would be to create configuration
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
#Configuration
public class CustomGlobalConfig {
#Value("${jwt.iss}")
private String issuer;
#Value("${jwt.jwk-uri}")
private String jwkUrl;
#Bean
CustomGlobal createFilterBean() {
return new CustomGlobal(this.issuer, this.jwkUrl);
}
}

Spring Zuul Header based routing hangs when downstream is not responsive for some time

I have implemented the below code to route requests based on headers to the respective downstreams.
When the downstream are unresponsive for sometime Zuul stops forwarding the requests, it does not forward the request when the downstream is up again.
There are no errors in the logs.
package com.uk.proxy.filter;
import ai.cuddle.sift.dataapi.proxy.Application;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;
#Component
public class RoutingFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
private static final String DEFAULT_DOWNSTREAM_GROUP = "SYSTEM";
public static final String HEADER_ORIGIN = "";
#Autowired
#Qualifier(value = "downstreamConfig")
private Map<String, String> downstreamMap;
#Override
public String filterType() {
return ROUTE_TYPE;
}
#Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 100;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String inputURI = request.getRequestURI();
String header = request.getHeader(HEADER_ORIGIN);
try {
String downstreamHost = getDownstreamHost(header);
LOGGER.info(" Header "+header+ " Downstream Host "+downstreamHost);
ctx.setRouteHost(new URL(downstreamHost));
ctx.put("requestURI", inputURI);
} catch (MalformedURLException e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}
private String getDownstreamHost(String header) {
if(header == null || header.isEmpty()){
LOGGER.warn("Header is null or empty");
}
String downstreamGroup = (header == null || header.isEmpty()) ? DEFAULT_DOWNSTREAM_GROUP : header.toUpperCase();
String downstreamHost;
if (downstreamMap.containsKey(downstreamGroup)) {
downstreamHost = downstreamMap.get(downstreamGroup);
} else {
LOGGER.error("Header "+header+" not found in config. DownstreamMap "+downstreamMap);
downstreamHost = downstreamMap.get(DEFAULT_DOWNSTREAM_GROUP);
}
return downstreamHost;
}
}
application.yaml
zuul:
ignoredPatterns:
- /manage/**
routes:
yourService:
path: /**
stripPrefix: false
serviceId: customServiceId
host:
connect-timeout-millis: 300000
socket-timeout-millis: 300000
ribbon:
eureka:
enabled: false
Spring cloud version: Hoxton.SR8.
Please let me know if anyone has faced this issue.

Rest easy response status + body

I have following method in my rest service:
#POST
#Path("/create")
#ResponseStatus(HttpStatus.CREATED)
#Consumes(MediaType.WILDCARD)
public String create( .... ) {.... return json;}
so I want to get a response with json in body and status code CREATED.
The problem is: I can't get a response the CREATED status.
The status code is allways OK, so it seems that "#ResponseStatus(HttpStatus.CREATED)" is just ignored...
Can somebody help me with it?
I'm using hibernate 4.1, spring 3.1 and resteasy 2.3
As far as I know, it's not possible to achieve this by annotating the method with #org.springframework.web.bind.annotation.ResponseStatus.
You can return javax.ws.rs.core.Response from your method:
return Response
.status(Response.Status.CREATED)
.entity("ok")
.build();
Or you can have org.jboss.resteasy.spi.HttpResponse injected, and set the status code directly.
There might be more ways of doing this, but I'm only aware of these two.
Working testcase:
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.interception.PostProcessInterceptor;
import org.junit.Assert;
import org.junit.Test;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
public class ResponseTest {
#Path("/")
public static class Service {
#Context HttpResponse response;
#GET
#Path("/1")
public Response createdUsingResponse() throws NotFoundException {
return Response
.status(Response.Status.CREATED)
.entity("ok")
.build();
}
#GET
#Path("/2")
public String created() throws NotFoundException {
response.setStatus(Response.Status.CREATED.getStatusCode());
return "ok";
}
}
public static class Interceptor implements PostProcessInterceptor {
#Context HttpResponse response;
#Override
public void postProcess(ServerResponse response) {
if(this.response.getStatus() != 0){
response.setStatus(this.response.getStatus());
}
}
}
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(new Service());
dispatcher
.getProviderFactory()
.getServerPostProcessInterceptorRegistry()
.register(new Interceptor());
{
MockHttpRequest request = MockHttpRequest.get("/1");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals(201, response.getStatus());
}
{
MockHttpRequest request = MockHttpRequest.get("/2");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals(201, response.getStatus());
}
}
}

Resources