In Confluence 9.3.1 REST requests don't work with our plugin installed, but in Confluence < 9.3.1 e.g. in 9.2.1 version all worked well

Yes, I solved the problem using my own CompatibilityRequestWrapper

    HttpServletRequestWrapper wrappedRequest;
    try {
      wrappedRequest = new RestReadingServletRequest((HttpServletRequest) request);
      if (isConfluenceApplication) {
        wrappedRequest = new CompatibilityRequestWrapper(wrappedRequest);
      }
    } catch (Exception e) {
      log.error(e);
      chain.doFilter(request, response);
      return;
    }

    chain.doFilter(wrappedRequest, response);

RestReadingServletRequest have logic to create multi-readable request that reads request body to temp var and overrides getReader and getInputStream to return this temp stored data


import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * This class in needed to fix error in Confluence 9.3.1: "Trailing token (of type START_OBJECT)
 * found after value: not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`" error.
 */
public class CompatibilityRequestWrapper extends HttpServletRequestWrapper {

  private static final FormattingLogger log =
      FormattingLogger.getLogger(CompatibilityRequestWrapper.class);

  private CompatibilityInputStream compatibilityInputStream;
  private CompatibilityBufferedReader compatibilityBufferedReader;

  public CompatibilityRequestWrapper(HttpServletRequest httpServletRequest) {
    super(httpServletRequest);
  }

  public ServletInputStream getInputStream() throws IOException {
    if (this.compatibilityInputStream == null) {
      this.compatibilityInputStream = new CompatibilityInputStream(super.getInputStream());
    }
    return this.compatibilityInputStream;
  }

  public BufferedReader getReader() throws IOException {
    if (this.compatibilityBufferedReader == null) {
      this.compatibilityBufferedReader = new CompatibilityBufferedReader(super.getReader());
    }

    return this.compatibilityBufferedReader;
  }

  private class CompatibilityInputStream extends ServletInputStream {
    private final ServletInputStream delegate;

    private CompatibilityInputStream(ServletInputStream delegate) {
      this.delegate = delegate;
    }

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

    @Override
    public int read(byte[] b) throws IOException {
      return this.delegate.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
      return this.delegate.read(b, off, len);
    }

    @Override
    public int readLine(byte[] b, int off, int len) throws IOException {
      return this.delegate.readLine(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
      return this.delegate.skip(n);
    }

    @Override
    public int available() throws IOException {
      return this.delegate.available();
    }

    @Override
    public void close() throws IOException {
      this.delegate.close();
    }

    @Override
    public void mark(int readlimit) {
      this.delegate.mark(readlimit);
    }

    @Override
    public void reset() throws IOException {
      this.delegate.reset();
    }

    @Override
    public boolean markSupported() {
      return this.delegate.markSupported();
    }
  }

  private class CompatibilityBufferedReader extends BufferedReader {
    private final BufferedReader delegate;

    private CompatibilityBufferedReader(BufferedReader delegate) {
      super(delegate);
      this.delegate = delegate;
    }

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

    @Override
    public String readLine() throws IOException {
      return this.delegate.readLine();
    }

    @Override
    public int read(char[] cbuf) throws IOException {
      return this.delegate.read(cbuf);
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
      return this.delegate.read(cbuf, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
      return this.delegate.skip(n);
    }

    @Override
    public boolean ready() throws IOException {
      return this.delegate.ready();
    }

    @Override
    public boolean markSupported() {
      return this.delegate.markSupported();
    }

    @Override
    public void mark(int readAheadLimit) throws IOException {
      this.delegate.mark(readAheadLimit);
    }

    @Override
    public void reset() throws IOException {
      this.delegate.reset();
    }

    @Override
    public void close() throws IOException {
      this.delegate.close();
    }
  }
}