integrate multiple service response in spring cloud gateway - spring

When I get request form path for example /bar is it possible in spring cloud gateway to call multiple microservices and integrate their result (for example JSON) and send as response of /bar ?
How can i do it?
thanks

You can use ProxyExchange to help you composition multiple responses.
An example given by Spring Cloud:
#RestController
#SpringBootApplication
public class GatewaySampleApplication {
#Value("${remote.home}")
private URI home;
#GetMapping("/test")
public ResponseEntity<?> proxy(ProxyExchange<byte[]> proxy) throws Exception {
return proxy.uri(home.toString() + "/image/png").get();
}
}
In this case it is only used to return the ResponseEntity, but you can use it however you like. In your case you can combine multiple ResponseEntities.

Related

Resolving POST /** request URL to full request URL using micrometer

With the micro-service architecture I have written a generic POST request handler which is consumed by all the micro-services. The post mapping in spring look like this:
#RestController
#RequestMapping(value = "/v1/", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
public class V1Controller {
#PostMapping(path = "/**")
public #ResponseBody Json post () {}
}
Now while I am consuming the metrics for this endpoint using micrometer I am only getting /v1/ as the endpoint in the metrics while I am sending the full URL like /v1/demo/foo from the calling service. I tried lot of the combination but it is not working. I have also added the WebMvcTagsProvider where I am listing to request and resolving the POST api calls.
#Bean
#SuppressWarnings("unchecked")
public WebMvcTagsProvider webMvcTagsProvider(ObjectMapper objectMapper) {
return new DefaultWebMvcTagsProvider() {
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
if ("POST".equals(request.getMethod())) {
Tag uriTag = Tag.of("uri", String.valueOf(request.getRequestURI()));
return Tags.of(WebMvcTags.method(request), uriTag, WebMvcTags.exception(exception), WebMvcTags.status(response));
}
return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.exception(exception), WebMvcTags.status(response));
}
};
}
Still it is resolving to /v1/ URL in the metrics. I tried googling alot but didn't find any solution. Thanks in advance.
The build in Spring Boot RequestMapping based metrics match on the annotations and add those as tags.
This is to avoid a tag explosion. Imagine a #RequestMapping for a path like user/{userId}, you would want to group all those calls together (user/1, user/2, user/3).
You'll want to create your own Timer in your post method that set that url tags, etc there.
If you decide to reuse the same metric name as the built in Spring Boot metric, you'll want to disable that one as well, so you don't double count those requests.

Testing a secure spring mvc restful webservice

I have a secure web service that I have been trying to test. The service looks something like this:
#GetMapping(value= {"/v1/getSomeStuff"})
public SomeResp getSomeStuff(final UsernamePasswordAuthenticationToken token, #RequestParam(name="searchString", required=true) String request) {
//do some stuff in here
}
The services are secured using Spring Security and I am trying to write an IT test, that will supply the required UsernamePasswordAuthenticationToken. I have tried doing this:
#Test
#Sql(scripts = { "/db-scripts/company-script.sql","/db-scripts/user-script.sql" })
#WithUserDetails(value="testuser")
public void testGetSomeStuff() {
//test some stuff
getRestTemplate().getForEntity("http://localhost:" + port + "/v1/getSomeStuff?searchString=foo", SomeResp.class);
}
The Sql scripts are executing correctly and the User Details Service is being called, but the token is not being passed to the service. I understand (now) that #WithUserDetails is for test method security and won't do what I want. My question then is, is there another method for testing secured web services using the various spring test classes?

Spring cloud contract best practice to handle unhappy path

We used to have wiremock for integration tests with both happy and unhappy paths. Now we are trying to move to Spring cloud contract based integration tests. Though, I could not find any document related to contracts for unhappy paths (status code over 400). And I did some POC with status code 4xx/5xx in response but it didnot work.
Anyone knows the best practice to handle unhappy paths in consumer side? or is it not supported at all for unhappy paths with status code over 400 with spring cloud contract?
Here is an example:
Producer side
Contract.make {
description 'get 404 when entity was not found'
request {
method GET()
url '/entities/0'
}
response {
status NOT_FOUND()
}
}
Client side
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SomeApplication.class)
#AutoConfigureStubRunner(ids = "io.app:entity:+:stubs:8080")
#AutoConfigureTestDatabase
public class EntityClientTest {
#Rule
public ExpectedException exception = ExpectedException.none();
#Autowired
private EntityClient entityClient; // This is a FeignClient
#Test
public void shouldThrowNotFoundWithInvalidId() {
exception.expect(FeignException.class);
exception.expectMessage("404");
entityClient.getById(0);
}
}
As you can see, the getById thrown a 404 because the contract says so.

Spring cloud stream messaging system(RabbitMQ) implementation using Rest Controller(Restful API)

From past few days i'm trying to implement the Spring cloud stream messaging system using RestController, but it is not happening through the current implementation.
For this sample code i'm going to add RestController
#EnableBinding(Source.class)
#EnableConfigurationProperties(TimeSourceOptionsMetadata.class)
public class TimeSource {
#Autowired
private TimeSourceOptionsMetadata options;
#InboundChannelAdapter(value = Source.OUTPUT)
public String timerMessageSource() {
return new SimpleDateFormat(this.options.getFormat()).format(new Date());
}
}
But the #InboundChannelAdapter cannot accept any parameters from RequestMapping Get Method URL.At the end what i need is to add message to the broker using Restful API Get method from api call. which is the best way to do it?, I couldn't figure out any best process from internet.
spring cloud team already provided a source application that listens for HTTP requests and emits the body as a message payload. If the Content-Type matches text/* or application/json, the payload will be a String, otherwise the payload will be a byte array.
github link
You can go with this or if you want to write it yourself, you can do it like below:
#RestController
#EnableBinding(Source.class)
public class RestSource {
#Autowired
private Source channels;
#RequestMapping(path = "/", method = POST, consumes = {"application/json" })
#ResponseStatus(HttpStatus.ACCEPTED)
public void handleRequest(#RequestBody String body, #RequestHeader(HttpHeaders.CONTENT_TYPE) Object contentType) {
sendMessage(body, contentType);
}
private void sendMessage(Object body, Object contentType) {
channels.output().send(MessageBuilder.createMessage(body,
new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
}
}

Spring Cloud : Using routing type filter in Zuul

I have 2 micro-services (Service A and Service B) built using Spring Boot, which gets routed through a Zuul Proxy also built as a Spring Boot app and I have checked that the Zuul proxy works just fine. However, what I am trying to do is to write a custom routing type ZuulFilter which should first route to Service A when a request comes in for Service B. Here is what I need assistance for:
I would like to know an example of how a routing filter looks like as I do not see anything after searching the internet. What I get are some examples of pre-filter and Netflix's documentation doesn't help much as well on that aspect.
Whether writing a custom route filter would mess up the original routing behavior of Zuul
I would construct a Feign client in the Zuul filter and make the call to service A using it. Feign will populate a ribbon load balancer to make the call in just the same way that Zuul does when proxying.
I had the same issue and this is what I came up with.
public class ServicesLegacyRouteFilter extends ZuulFilter {
private ServiceB serviceB;
public ServiceLegacyRouteFilter(ServiceB serviceB) {
this.serviceB = serviceB;
}
#Override
public String filterType() {
return ROUTE_TYPE;
}
#Override
public int filterOrder() {
return 10;
}
#Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
if ("serviceA".equals(ctx.get("serviceId"))) {
//call Service B here and use return type to set
//the final destination service
String destination = serviceB.routeWhere();
ctx.set("serviceId", destination);
return true;
}
return false;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// Or call ServiceB here to make your determination on
// the final destination.
String destination = serviceB.routeWhere();
ctx.set("serviceId", destination);
return null;
}
}
My actual production use case was more complicated on the routing of course, but this is the basics of how I was able to change routes based on what was coming in and how to take advantage of Zuul to get it out to the correct service.

Resources