Ir ao conteúdo

Mateus Kuczmanski

Membro Júnior
  • Posts

    2
  • Cadastrado em

  • Última visita

  1. Pouca gente fala, mas nenhum framework Java web tradicional (nem mesmo Spring) suporta de forma nativa multipart/mixed. Em um projeto onde era necessário enviar JSON + arquivos no mesmo request (padrão moderno em APIs RESTful), o Tomcat simplesmente quebrava. VRaptor? Nem passava dos filtros. A solução? Esse filtro intercepta requisições multipart/mixed, duplica o corpo da requisição e entrega um novo ServletInputStream para que o restante da aplicação funcione normalmente — como se fosse suportado nativamente. Isso desbloqueia: Integração com APIs modernas Uploads + metadados num só request Logging de payloads completos Parsing customizado mesmo em ambientes legados Recentemente, enfrentei uma integração com dispositivos Intelbras que envia requisições com o cabeçalho Content-Type: multipart/mixed. Como o InputStream de uma requisição HTTP pode ser lido apenas uma vez, precisei implementar um filtro para capturar e duplicar o corpo da requisição, garantindo que ele pudesse ser reutilizado por outros filtros e controllers no fluxo da aplicação. Esse filtro intercepta rotas específicas, armazena o corpo da requisição em memória, e o repassa via HttpServletRequestWrapper, utilizando um novo ServletInputStream baseado em ByteArrayInputStream. Incompatíveis com multipart/mixed (sem suporte nativo): VRaptor 3.x Não reconhece multipart/mixed, apenas multipart/form-data Documentação + issues + testes Spring MVC (v5) Suporte limitado apenas a multipart/form-data Documentação Spring Multipart Jakarta REST (JAX-RS) Sem suporte nativo a multipart/mixed Documentação Jakarta REST Tomcat getParts() não funciona corretamente com multipart/mixed Documentação Tomcat ASP.NET Core Não reconhece multipart/mixed como formato de upload padrão Documentação ASP.NET Flask Sem suporte embutido para multipart/mixed Documentação Flask Django Limitado a multipart/form-data via request.FILES Documentação Django Node.js (Express + multer) Multer não lida com multipart/mixed Documentação Multer/Node.js PHP puro Suporte apenas a multipart/form-data via $_FILES Documentação PHP Solução no VRaptor 3.x VRaptor 3.x – Limitações com multipart/mixed Fonte: Documentação oficial do VRaptor 3, histórico de issues e testes práticos. Diagnóstico O VRaptor 3.x possui suporte nativo apenas para multipart/form-data, utilizando o Servlet3MultipartInterceptor. O tipo multipart/mixed não é reconhecido nem tratado pelo framework, o que inviabiliza seu uso em uploads com múltiplas partes heterogêneas (ex: arquivos + JSON no mesmo corpo). Limitações técnicas identificadas O interceptor atual depende do request.getParts(), que não processa multipart/mixed. Não há documentação ou trechos no código que tratem multipart/mixed. VRaptor 3 já apresenta restrições com o próprio multipart/form-data em containers como Tomcat 7 segundo publicações. Assumindo um projeto padrão Maven com VRaptor 3.x ou qualquer app Java Web: src/ └── main/ ├── java/ │ └── com/ │ └── suaempresa/ │ └── seuprojeto/ │ └── filter/ │ └── MultipartMixedFilter.java ├── resources/ └── webapp/ └── WEB-INF/ └── web.xml Classe: MultipartMixedFilter.java // Importações básicas para manipulação de streams, datas e exceções import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; // Importações de classes da API Servlet import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ReadListener; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; // Anotação que registra o filtro para todas as requisições que começam com /api/exemplo @WebFilter(urlPatterns = "/api/exemplo/*") public class MultipartMixedFilter implements Filter { // Método chamado quando o filtro é inicializado — não estamos usando aqui @Override public void init(FilterConfig filterConfig) {} // Método principal do filtro, intercepta e processa a requisição @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Faz o cast para HttpServletRequest para ter acesso a métodos específicos da API HTTP HttpServletRequest httpRequest = (HttpServletRequest) request; // Obtém o tipo de conteúdo da requisição String contentType = httpRequest.getContentType(); // Verifica se é uma requisição multipart/mixed if (contentType != null && contentType.contains("multipart/mixed")) { // Cria um buffer para armazenar o corpo da requisição ByteArrayOutputStream buffer = new ByteArrayOutputStream(); // Lê o corpo da requisição original e grava no buffer try (InputStream inputStream = httpRequest.getInputStream()) { byte[] temp = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(temp)) != -1) { buffer.write(temp, 0, bytesRead); } } // Transforma o buffer em um array de bytes byte[] bodyBytes = buffer.toByteArray(); // Cria um wrapper para a requisição original, permitindo reutilização do InputStream HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(httpRequest) { // Sobrescreve o getInputStream para retornar o corpo armazenado @Override public ServletInputStream getInputStream() { return new ServletInputStream() { private final ByteArrayInputStream bais = new ByteArrayInputStream(bodyBytes); @Override public int read() { return bais.read(); // Retorna byte a byte } @Override public boolean isFinished() { return bais.available() == 0; // Fim do stream } @Override public boolean isReady() { return true; // Sempre pronto pra leitura } @Override public void setReadListener(ReadListener readListener) { // Não implementado (poderia ser usado para leitura assíncrona) } }; } // Garante que o content length da nova requisição seja compatível @Override public int getContentLength() { return bodyBytes.length; } @Override public long getContentLengthLong() { return bodyBytes.length; } }; // Passa a requisição "wrappeada" adiante na cadeia de filtros chain.doFilter(wrappedRequest, response); return; } // Se não for multipart/mixed, continua o fluxo normalmente chain.doFilter(request, response); } // Método chamado na destruição do filtro — não estamos usando @Override public void destroy() {} } Classe: web.xml (Adicionar no web.xml) <!-- Definição do filtro MultipartMixedFilter. Esse filtro permite que o corpo da requisição multipart/mixed (como uploads de arquivos) seja lido mais de uma vez, o que é útil em logs, debug, validações ou integrações. --> <filter> <!-- Nome identificador do filtro --> <filter-name>MultipartMixedFilter</filter-name> <!-- Caminho completo (fully qualified) da classe Java que implementa o filtro --> <filter-class>com.suaempresa.seuprojeto.filter.MultipartMixedFilter</filter-class> </filter> <!-- Mapeamento do filtro para interceptar todas as requisições da aplicação. Aqui você está dizendo que o filtro será executado para qualquer URL que entrar no sistema. --> <filter-mapping> <!-- Nome do filtro definido acima --> <filter-name>MultipartMixedFilter</filter-name> <!-- Padrão de URL que esse filtro vai interceptar. /* significa que será aplicado globalmente a todas as rotas --> <url-pattern>/*</url-pattern> </filter-mapping> Impacto real do MultipartMixedFilter no projeto Benefícios práticos 1. Permite reprocessamento do corpo da requisição - O HttpServletRequest.getInputStream() só pode ser lido uma vez por padrão. - Esse filtro “clona” o corpo (bodyBytes) e permite múltiplas leituras. - Útil para logs, validações de segurança, auditoria, etc. 2. Suporte a requisições complexas (ex: multipart/mixed) - Algumas APIs externas, especialmente integradas com sistemas legados ou gateways, usam multipart/mixed (em vez de multipart/form-data). - O filtro garante que sua aplicação aceite e trate corretamente esse tipo de conteúdo. 3. Evita quebra em filtros, interceptadores ou logs - Sem ele, se outro filtro ler o InputStream, o controller depois não consegue mais acessar. - Com esse filtro, você blinda o comportamento. Riscos e cuidados 1. Mais uso de memória - Toda requisição com multipart/mixed é armazenada em memória RAM (byte array). - Requisições grandes (>10MB) podem causar estouro de memória (OOM) sob alta carga. - Se a aplicação processa arquivos ou JSONs muito grandes, precisa limitar o tamanho. 2. Potencial latência maior - A leitura do input inteiro e cópia para buffer pode aumentar o tempo de resposta. - Impacto baixo na maioria dos casos, mas relevante em alta escala. 3. Pode interferir com outros filtros - Se outro filtro também tenta ler/modificar o input, pode haver conflito. - Recomendado que este filtro seja o primeiro da cadeia. Situação Usar o filtro? Precisa logar o body da requisição Sim Usa APIs com multipart/mixed Sim Precisa ler o body no filtro e no controller Sim Só processa application/json comum Não precisa Processa arquivos grandes (>10MB) Com limite Rodando em ambiente com pouca RAM Avaliar uso E você? Já lidou com integrações que usam multipart/mixed? Como resolveu? Teve que contornar limitações no framework também? Tem libs, ou exemplos práticos que usou?

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...