Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to transform client request body with a zuul filter?

I want to use Zuul to authenticate, transform and forward request from a client to internal services. The goal is to hide from the client a legacy API.

What I have in mind is: a client sends a POST request with the JSON representation of an object A to the API Gateway where Zuul is embedded. The API Gateway transforms the body from A to LegacyA and send it to the internal service.

For example, I search a way to transform the following JSON:

["hello","world"]

in this JSON:

{hashCode("hello"):"hello", hashCode("world"):"world")}

I want to use a pre-filter. But I have problem to rewrite a valid request.

Do you have any idea how can I do that?

I have written this filter:

public class RestZuulFilter extends ZuulFilter {

    private final ObjectMapper objectMapper;

    @Autowired
    public RestZuulFilter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 100;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();

        HttpServletRequestWrapper wrapper = new MyWrapper(ctx.getRequest());
        ctx.setRequest(wrapper);

        return null;
    }

    class MyWrapper extends HttpServletRequestWrapper {

        /**
         * Constructs a request object wrapping the given request.
         *
         * @param request The request to wrap
         * @throws IllegalArgumentException if the request is null
         */
        public MyWrapper(HttpServletRequest request) {
            super(request);
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            ServletInputStream inputStream = this.getRequest().getInputStream();

            List<String> test = objectMapper.readValue(inputStream, new TypeReference<List<String>>() {
            });
            Map<Integer, String> result = test.stream()
                    .collect(Collectors.toMap(String::hashCode, str -> str));

            byte[] json = objectMapper.writeValueAsBytes(result);
            ServletInputStream response = new ServletInputStreamWrapper(json);

            return response;
        }
    }
}

The problem that I have is with the Content-Length which is not updated accordinally.

like image 870
Franck Anso Avatar asked Mar 11 '23 01:03

Franck Anso


1 Answers

Take a look at the following example of a request body being transformed. I suppose this should work for you.

    InputStream in = (InputStream) context.get("requestEntity");
    if (in == null) {
        in = context.getRequest().getInputStream();
    }
    String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
    body = body.toUpperCase();
    context.set("requestEntity", new ByteArrayInputStream(body.getBytes("UTF-8")));

For the complete class:

https://github.com/spring-cloud-samples/sample-zuul-filters/blob/master/src/main/java/org/springframework/cloud/samplezuulfilters/UppercaseRequestEntityFilter.java

like image 100
Manuel Palacio Avatar answered Mar 22 '23 23:03

Manuel Palacio