Spring Cloud Gateway MVC : Multipart + Query Param is not working
Describe the bug Send multipart request and also query parameter, the query parameter is not forwarded to backend service
Spring Cloud Version : 2023.0.3
Sample
spring.cloud.gateway.mvc.routes[0].id=NOCHECK
spring.cloud.gateway.mvc.routes[0].uri=https://engvxhu8p8n2b.x.pipedream.net
spring.cloud.gateway.mvc.routes[0].predicates[0]=Path=/backend/nocheck/**
Example Curl :
curl --location 'http://localhost:8089/backend/nocheck/eko?orderId=27006607934&storeId=10001&clientId=off2on-mobile&channelId=off2on-mobile&requestId=7bcf27c6-ef84-4715-9a9b-a77ff77ddb43&username=b80edb9d-790e-4563-aaf8-a61dd128a653%40blibli' \
--header 'Content-Type: multipart/form-data' \
--form 'name="Eko"' \
--form 'hello="test"'
Backend Received :
current solution, is to change MultipartResolver from GatewayMvcMultipartResolver to custom resolver
package com.gdn.rnd.backend.gateway.external.filter.multipart;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* This class is a custom class because {@link org.springframework.cloud.gateway.server.mvc.handler.GatewayMvcMultipartResolver}
* still has bug on query parameter
*
* @see org.springframework.cloud.gateway.server.mvc.handler.GatewayMvcMultipartResolver
*/
@Component
public class BlibliMultipartResolver extends StandardServletMultipartResolver {
@Override
public boolean isMultipart(HttpServletRequest request) {
return super.isMultipart(request);
}
@Override
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
String queryString = request.getQueryString();
StringBuffer requestURL = request.getRequestURL();
if (StringUtils.hasText(queryString)) {
requestURL.append('?').append(queryString);
}
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(requestURL.toString());
MultiValueMap<String, String> queryParams = uriComponentsBuilder.build().getQueryParams();
return new BlibliMultipartResolver.GatewayMultipartHttpServletRequest(request, queryParams);
}
private static boolean isGatewayRequest(HttpServletRequest request) {
return request.getAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR) != null
|| request.getAttribute(MvcUtils.GATEWAY_REQUEST_URL_ATTR) != null;
}
/**
* StandardMultipartHttpServletRequest wrapper that will not parse multipart if it is
* a gateway request. A gateway request has certain request attributes set.
*/
static class GatewayMultipartHttpServletRequest extends StandardMultipartHttpServletRequest {
private final MultiValueMap<String, String> queryParams;
GatewayMultipartHttpServletRequest(HttpServletRequest request, MultiValueMap<String, String> queryParams) {
super(request, true);
this.queryParams = queryParams;
}
@Override
protected void initializeMultipart() {
if (!isGatewayRequest(getRequest())) {
super.initializeMultipart();
}
}
@Override
public Map<String, String[]> getParameterMap() {
if (isGatewayRequest(getRequest())) {
return getQueryParameterMap();
}
return super.getParameterMap();
}
private Map<String, String[]> getQueryParameterMap() {
Map<String, String[]> result = new LinkedHashMap<>();
Enumeration<String> names = getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
result.put(name, getParameterValues(name));
}
return result;
}
@Override
public String getParameter(String name) {
return this.queryParams.getFirst(name);
}
@Override
public Enumeration<String> getParameterNames() {
return Collections.enumeration(this.queryParams.keySet());
}
@Override
public String[] getParameterValues(String name) {
return StringUtils.toStringArray(this.queryParams.get(name));
}
}
}
i have the same issue. This problem appears to be because getParameterMap Method of GatewayMultipartHttpServletRequest is return emptyMap. i replaced the MultipartResolver Bean of Gateway MultipartAutoConfiguration temporarily Like the solution given by khannedy
Duplicate of https://github.com/spring-cloud/spring-cloud-gateway/issues/3220