Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add headers and path dynamically based on request body #1022

Closed
sivareddygithub opened this issue Apr 22, 2019 · 6 comments
Closed

How to add headers and path dynamically based on request body #1022

sivareddygithub opened this issue Apr 22, 2019 · 6 comments

Comments

@sivareddygithub
Copy link

I have a requirement where we need to develop Spring API gate way to consume soap request (xml in the form of string) convert request body to JSON and hit Rest services which consume Json object and response Json object need to convert to XML in the form of sting and send it back to consumer. Up to here we are good but my question how to add or modify headers and path based on request body. Is there any way in modify request method to add headers and path dynamically?

@sivareddygithub sivareddygithub changed the title How to add dynamic headers and path based on request body How to add headers and path dynamically based on request body Apr 22, 2019
@ryanjbaxter
Copy link
Contributor

It would probably be a bit tricky but there are some pieces in the code that will help you get started. Reading the body in a way that allows code downstream to read the body as well is the trickiest part. There is code in ReadBodyPredicateFactory which you can use to show you how to do this correctly. https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/ReadBodyPredicateFactory.java

Once you have the body though you can convert the XML to JSON and also modify the with the correct path and headers. You can do something similar in a post filter for when returning the response.

@spring-projects-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@sivareddygithub
Copy link
Author

sivareddygithub commented May 8, 2019 via email

@sivareddygithub
Copy link
Author

Please don’t close this issue I will update code here, if any draw backs or if it not productive or scalable, I can get inputs after seeing code so that I can refactor my code and if any have same requirement to modify request and response and based on request and if they need to set path dynamically can use this code.

@sivareddygithub
Copy link
Author

  1. Created Spring boot application using https://start.spring.io/

  2. Downloaded and set up the application in STS.

  3. Created config folder and added below below java file.

    RouteLocator Code:

     	@Configuration
     		public class CloudGatewayRouteConfig {
    
     			@Autowired
     			MappingUtility utilty;
     			
     			@Bean
     			public RouteLocator myRoutes(RouteLocatorBuilder builder) {
     				return builder.routes()
     				    //This is the route where all requests are intercepted for different operations from my Soap service and route it to rest services
     						.route(r -> r.path("/[path]/**").and().method(HttpMethod.POST)
     								.filters(f -> f.stripPrefix(1)
     										.addRequestHeader(CloudGatewayConstants.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
     										/*Below two lines of code iam removing and adding header because if i won't remove, 
     											filter is appending to existing one which in turn causing exception. 
     											For example incoming type is text/xml and Rest is allowing only application/json
     											so i followed like below to resolve the issue
     										*/
     										.removeRequestHeader(CloudGatewayConstants.CONTENT_TYPE)
     										.addRequestHeader(CloudGatewayConstants.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
     										.addRequestHeader(CloudGatewayConstants.ACCEPT_LANGUAGE, CloudGatewayConstants.EN_US)
     										/*Below line is not required because we are setting path in custom filter. 
     										But i added this because some times i observed one weired scenario if nothing set 
     										it is taking the path of original request and it is not allowing to modify. 
     										Any dummy value id enough to avoid above scenario.
     										*/
     										.setPath("[path]")
     										/*
     										This is where i converted from xml to json object in preGatewayFilterFactory(exchange,request) private method.
     										This method call MappingUtility where i wrote custom code to convert and expects Map object in return 
     										see code below in preGatewayFilterFactory peivate method
     										*/
     										.modifyRequestBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE,
     												(exchange, request) -> {
     													return Mono.just(preGatewayFilterFactory(exchange,request));
     												})
     										// Custom filter to set path which i set in preGatewayFilterFactory private method as expalined above
     										.filter(new SetCustomPathGatewayFilterFactory().apply(c -> c.setTemplate(CloudGatewayConstants.URI_PATH_HEADER)))
     										/*This is where i converted from Json object to XML 
     										and send back to original request in postGatewayFilterFactory(exchange,response) private method.
     										In this private method there is one line of code where i am getting executedMethod from header which is set in
     										preGatewayFilterFactory private method in modifyRequestBody
     										*/
     										.modifyResponseBody(String.class, String.class, MediaType.TEXT_XML_VALUE,
     												(exchange, response) -> {
     													return Mono.just(postGatewayFilterFactory(exchange,response));
     												})
     										 /*
     										 Below two lines of code is same as i explained above while sending back we need to set original content.
     										 */
     										.removeResponseHeader(CloudGatewayConstants.CONTENT_TYPE)
     										.addResponseHeader(CloudGatewayConstants.CONTENT_TYPE, MediaType.TEXT_XML_VALUE)
     								).uri("https://[EndPointURL]")).build();
     			}
     			
     			private String preGatewayFilterFactory(ServerWebExchange exchange, String request) {
     				String modifiedRequestBody = null;
     				//In below line i am calling Utility class where i am converting and returning map 
     				//to set custom header from request body and path based on request body
     				Map<String, Object> requestDataMap = utilty.modifyRequestBodyToCustomObject(request);
     				if(requestDataMap != null && !requestDataMap.isEmpty()){
     					exchange.getRequest().mutate().header(CloudGatewayConstants.CUSTOM_HEADER,
     							""+requestDataMap.get(CloudGatewayConstants.CUSTOM_HEADER)).build();
     					exchange.getRequest().mutate().header(CloudGatewayConstants.URI_PATH_HEADER, 
     							""+requestDataMap.get(CloudGatewayConstants.URI_PATH_HEADER)).build();
     					exchange.getRequest().mutate().header(CloudGatewayConstants.EXECUTED_METHOD, 
     							""+requestDataMap.get(CloudGatewayConstants.EXECUTED_METHOD)).build();
     					
     					modifiedRequestBody = ""+requestDataMap.get(CloudGatewayConstants.MODIFIED_REQUEST_BODY);
     				}
     				
     				return modifiedRequestBody;
     			}
    
     			private String postGatewayFilterFactory(ServerWebExchange exchange, String response) {
     			  //Below line helps to know which method is executed so that while converting we can use same objects and logic
     			  // This Header i set in above method
     				String executedMethod = exchange.getRequest().getHeaders().getFirst(CloudGatewayConstants.EXECUTED_METHOD);
     				return utilty.modifyResponseBodyToCustomObject(response, executedMethod);
     			}
     			
     		}
    
  4. Created custom filter which is calling in third point above to set path based on request body, please see code in between modifyRequestBody and modifyResponseBody.

    Custom path Filter code:

     		  public class SetCustomPathGatewayFilterFactory extends AbstractGatewayFilterFactory<SetCustomPathGatewayFilterFactory.Config> {
     				    
     						public SetCustomPathGatewayFilterFactory() {
     				        super(Config.class);
     				    }
    
     				    @Override
     				    public GatewayFilter apply(Config config) {
     				        // TODO: Implement
     				    	return (exchange, chain) -> {
     				            //If you want to build a "pre" filter you need to manipulate the
     				            //request before calling chain.filter
     				    			 String path = exchange.getRequest().getHeaders().getFirst(config.template);
     				    			 System.out.println("SetCustomPathGatewayFilterFactory="+path);
     				    		
     				            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
     				          
     				            ServerHttpRequest request = builder.path(path).build();
     				            
     				            return chain.filter(exchange.mutate().request(request).build());
     				            
     						};
     				    }
    
     				    public static class Config {
     				        private String template;
    
     						public String getTemplate() {
     							return template;
     						}
    
     						public void setTemplate(String template) {
     							this.template = template;
     						}
    
     				    }
     				}
    

@sivareddygithub
Copy link
Author

@ryanjbaxter, Please review my code and let me know if any drawbacks and suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants