Spring form input values return null after read it once in filter, despite using request wrapper

926 Views Asked by At

In my spring-boot project, I use freemarker templates for sample forms. I needed to add filter in order to read payload and do some stuff. I know if you read payload in filter, you need to reset request body. Because it can be read once. Since I encountered this problem before, I knew that I must have used wrapper. I expected solve my problem as before. However, in the controller, all fields in input objects are null.

What am I missing in here ?

My filter:

public class KfsInMsgFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

       HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;

        Map<String, String[]> extraParams = new TreeMap<String, String[]>();
        WrappedRequest wrappedRequest = new WrappedRequest(request, extraParams);

        String body = IOUtils.toString(new BufferedReader(new InputStreamReader(wrappedRequest.getInputStream(), Constants.UTF_8)));

        // doing some stuff using body
        // ....

        // resetting payload
        wrappedRequest.resetStream(body.getBytes(Constants.UTF_8));

        ...

    } 
}

WrappedRequest class:


@Slf4j
public class WrappedRequest extends HttpServletRequestWrapper {
    private final Map<String, String[]> modifiableParameters;
    private ResettableServletInputStream servletStream;
    private byte[] rawData;
    private HttpServletRequest request;
    private String payload;


    /**
     * Create a new request wrapper that will merge additional parameters into
     * the request object without prematurely reading parameters from the
     * original request.
     *
     * @param request
     * @param additionalParams
     */
    public WrappedRequest(final HttpServletRequest request,
                          final Map<String, String[]> additionalParams) {
        super(request);
        this.request = request;
        this.modifiableParameters = new TreeMap<String, String[]>();
        this.modifiableParameters.putAll(additionalParams);
        this.servletStream = new ResettableServletInputStream();
    }

    /**
     * @param newRawData
     */
    public void resetStream(byte[] newRawData) {
        servletStream.stream = new ByteArrayInputStream(newRawData);
    }

    /**
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (rawData == null) {
            rawData = IOUtils.toByteArray(this.request.getReader());
            servletStream.stream = new ByteArrayInputStream(rawData);
        }
        return servletStream;
    }

    /**
     * @return
     * @throws IOException
     */
    @Override
    public BufferedReader getReader() throws IOException {
        if (rawData == null) {
            rawData = IOUtils.toByteArray(this.request.getReader());
            servletStream.stream = new ByteArrayInputStream(rawData);
        }
        return new BufferedReader(new InputStreamReader(servletStream, Constants.UTF_8));
    }

    /**
     * @return
     */
    private String getBodyAsString() {
        StringBuffer buff = new StringBuffer();
        buff.append(" BODY_DATA START [ ");
        char[] charArr = new char[getContentLength()];
        try {
            BufferedReader reader = new BufferedReader(getReader());
            reader.read(charArr, 0, charArr.length);
            reader.close();
        } catch (IOException e) {
            log.error("", e);
        }
        buff.append(charArr);
        buff.append(" ] BODY_DATA END ");
        return buff.toString();
    }

    /**
     * @return
     */
    public String getPayload() {
        return payload;
    }

    /**
     * @param payload
     */
    public void setPayload(String payload) {
        this.payload = payload;
    }

    private static class ResettableServletInputStream extends ServletInputStream {

        private InputStream stream;

        @Override
        public int read() throws IOException {
            return stream.read();
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener readListener) {

        }
    }
}

Body I expected to get in controller: filter

What I get: controller

@PostMapping(value = "/edit")
    public String editPlatform(EditInfo editInfo, Model model) {
        Optional<Platform> p = platformService.findById(editInfo.getId());
        List<SafeCustodyOffice> officeList = safeCustodyOfficeService.getAll();

        if (p.isPresent()) {
            model.addAttribute("platform", p.get());
            model.addAttribute("offices", officeList);
            return "platform-edit";
        } else {
            throw new KfsException(ErrorCodes.KFS19);
        }
    }

Important Edit:

I discovered someting I found interesting and gives me clues about the problem. This may be makes more sense for anybody but me.

I see that the content type of input changes the result like this:

enter image description here

Is there any workaround to make row 5 combination work like row 3?

0

There are 0 best solutions below