Support configuring multiple values in http header/query parameter/form-data #246
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package we.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -36,6 +37,10 @@ import org.springframework.util.MultiValueMap;
|
||||
*/
|
||||
public class MapUtil {
|
||||
|
||||
private static final String KEY = "key";
|
||||
|
||||
private static final String VALUE = "value";
|
||||
|
||||
public static HttpHeaders toHttpHeaders(Map<String, Object> params) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
@@ -66,7 +71,6 @@ public class MapUtil {
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
public static MultiValueMap<String, String> toMultiValueMap(Map<String, Object> params) {
|
||||
MultiValueMap<String, String> mvmap = new LinkedMultiValueMap<>();
|
||||
|
||||
@@ -124,15 +128,16 @@ public class MapUtil {
|
||||
return mvmap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract form data from multipart map exclude file
|
||||
*
|
||||
* @param params
|
||||
* @param fileKeyPrefix
|
||||
* @param filePartMap Map
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> extractFormData(MultiValueMap<String, Part> params, String fileKeyPrefix, Map<String, FilePart> filePartMap) {
|
||||
public static Map<String, Object> extractFormData(MultiValueMap<String, Part> params, String fileKeyPrefix,
|
||||
Map<String, FilePart> filePartMap) {
|
||||
HashMap<String, Object> m = new HashMap<>();
|
||||
if (params == null || params.isEmpty()) {
|
||||
return m;
|
||||
@@ -174,11 +179,13 @@ public class MapUtil {
|
||||
|
||||
/**
|
||||
* Replace file field with FilePart object
|
||||
*
|
||||
* @param params
|
||||
* @param fileKeyPrefix
|
||||
* @param filePartMap
|
||||
*/
|
||||
public static void replaceWithFilePart(MultiValueMap<String, Object> params, String fileKeyPrefix, Map<String, FilePart> filePartMap) {
|
||||
public static void replaceWithFilePart(MultiValueMap<String, Object> params, String fileKeyPrefix,
|
||||
Map<String, FilePart> filePartMap) {
|
||||
if (params == null || params.isEmpty() || filePartMap == null || filePartMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -255,9 +262,24 @@ public class MapUtil {
|
||||
return rs;
|
||||
}
|
||||
|
||||
public static MultiValueMap<String, Object> upperCaseKey(MultiValueMap<String, Object> m) {
|
||||
MultiValueMap<String, Object> rs = new LinkedMultiValueMap<>();
|
||||
|
||||
if (m == null || m.isEmpty()) {
|
||||
return rs;
|
||||
}
|
||||
|
||||
for (Entry<String, List<Object>> entry : m.entrySet()) {
|
||||
rs.put(entry.getKey().toUpperCase(), entry.getValue());
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value by path,support multiple levels,eg:a.b.c <br>
|
||||
* Do NOT use this method if field name contains a dot <br>
|
||||
*
|
||||
* @param data
|
||||
* @param path
|
||||
* @param value
|
||||
@@ -287,6 +309,7 @@ public class MapUtil {
|
||||
/**
|
||||
* Get value by path, support multiple levels,eg:a.b.c <br>
|
||||
* Do NOT use this method if field name contains a dot <br>
|
||||
*
|
||||
* @param data
|
||||
* @param path
|
||||
* @return
|
||||
@@ -316,6 +339,7 @@ public class MapUtil {
|
||||
|
||||
/**
|
||||
* Merge maps, merge src to target
|
||||
*
|
||||
* @param target
|
||||
* @param src
|
||||
* @return
|
||||
@@ -339,4 +363,70 @@ public class MapUtil {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert list to map and merge multiple values<br>
|
||||
* Example: <br>
|
||||
* List as:<br>
|
||||
* [{"key": "abc", "value": "aaa"},{"key": "abc", "value": "xyz"},{"key":
|
||||
* "a123", "value": 666}]<br>
|
||||
* Merge Result:<br>
|
||||
* {"abc": ["aaa","xyz"], "a123": 666} <br>
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> list2Map(Object obj) {
|
||||
// Compatible with older version configuration
|
||||
if (obj instanceof Map) {
|
||||
return (Map<String, Object>) obj;
|
||||
}
|
||||
if (obj instanceof List) {
|
||||
Map<String, Object> rs = new HashMap<>();
|
||||
List<Map<String, Object>> list = (List<Map<String, Object>>) obj;
|
||||
if (list == null || list.size() == 0) {
|
||||
return rs;
|
||||
}
|
||||
for (Map<String, Object> m : list) {
|
||||
String k = m.get(KEY).toString();
|
||||
Object v = m.get(VALUE);
|
||||
if (rs.containsKey(k)) {
|
||||
List<Object> vals = null;
|
||||
if (rs.get(k) instanceof List) {
|
||||
vals = (List<Object>) rs.get(k);
|
||||
} else {
|
||||
vals = new ArrayList<>();
|
||||
vals.add(rs.get(k));
|
||||
}
|
||||
vals.add(v);
|
||||
rs.put(k, vals);
|
||||
} else {
|
||||
rs.put(k, v);
|
||||
}
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert list to MultiValueMap<br>
|
||||
* List format example:<br>
|
||||
* [{"key": "abc", "value": "aaa"},{"key": "abc", "value": "xyz"},,{"key":
|
||||
* "a123", "value": 666}]<br>
|
||||
*
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
public static MultiValueMap<String, Object> listToMultiValueMap(List<Map<String, Object>> list) {
|
||||
MultiValueMap<String, Object> mvmap = new LinkedMultiValueMap<>();
|
||||
if (list == null || list.size() == 0) {
|
||||
return mvmap;
|
||||
}
|
||||
for (Map<String, Object> m : list) {
|
||||
mvmap.add(m.get(KEY).toString(), m.get(VALUE));
|
||||
}
|
||||
return mvmap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import reactor.core.publisher.Mono;
|
||||
import we.exception.ExecuteScriptException;
|
||||
import we.exception.RedirectException;
|
||||
import we.exception.StopAndResponseException;
|
||||
import we.fizz.exception.FizzRuntimeException;
|
||||
import we.flume.clients.log4j2appender.LogService;
|
||||
import we.legacy.RespEntity;
|
||||
import we.util.JacksonUtils;
|
||||
@@ -81,6 +82,19 @@ public class FilterExceptionHandlerConfig {
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes())));
|
||||
}
|
||||
}
|
||||
if (t instanceof FizzRuntimeException) {
|
||||
FizzRuntimeException ex = (FizzRuntimeException) t;
|
||||
resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
RespEntity rs = null;
|
||||
String reqId = exchange.getRequest().getId();
|
||||
if (ex.getStepContext() != null && ex.getStepContext().returnContext()) {
|
||||
rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId, ex.getStepContext());
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(JacksonUtils.writeValueAsString(rs).getBytes())));
|
||||
} else {
|
||||
rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId);
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes())));
|
||||
}
|
||||
}
|
||||
Mono<Void> vm;
|
||||
Object fc = exchange.getAttributes().get(WebUtils.FILTER_CONTEXT);
|
||||
if (fc == null) { // t came from flow control filter
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
package we.fizz.exception;
|
||||
|
||||
import we.fizz.StepContext;
|
||||
|
||||
public class FizzRuntimeException extends RuntimeException {
|
||||
|
||||
private StepContext<String, Object> stepContext;
|
||||
|
||||
public FizzRuntimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FizzRuntimeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.setStackTrace(cause.getStackTrace());
|
||||
}
|
||||
|
||||
public FizzRuntimeException(String message, StepContext<String, Object> stepContext) {
|
||||
super(message);
|
||||
this.stepContext = stepContext;
|
||||
}
|
||||
|
||||
public FizzRuntimeException(String message, Throwable cause, StepContext<String, Object> stepContext) {
|
||||
super(message, cause);
|
||||
this.setStackTrace(cause.getStackTrace());
|
||||
this.stepContext = stepContext;
|
||||
}
|
||||
|
||||
public StepContext<String, Object> getStepContext() {
|
||||
return stepContext;
|
||||
}
|
||||
|
||||
public void setStepContext(StepContext<String, Object> stepContext) {
|
||||
this.stepContext = stepContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ package we.fizz.input;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.noear.snack.ONode;
|
||||
|
||||
import we.constants.CommonConstants;
|
||||
import we.fizz.StepContext;
|
||||
import we.fizz.exception.FizzRuntimeException;
|
||||
import we.util.MapUtil;
|
||||
|
||||
/**
|
||||
@@ -105,14 +105,15 @@ public class PathMapping {
|
||||
return target.toObject(Map.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ONode transform(ONode ctxNode, Map<String, Object> rules, boolean supportMultiLevels) {
|
||||
ONode target = ONode.load(new HashMap());
|
||||
if (rules.isEmpty()) {
|
||||
return target;
|
||||
}
|
||||
|
||||
Map<String, String> rs = new HashMap<>();
|
||||
Map<String, String> types = new HashMap<>();
|
||||
Map<String, Object> rs = new HashMap<>();
|
||||
Map<String, Object> types = new HashMap<>();
|
||||
for (Entry<String, Object> entry : rules.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
String val = (String) entry.getValue();
|
||||
@@ -123,6 +124,25 @@ public class PathMapping {
|
||||
} else {
|
||||
rs.put(entry.getKey(), val);
|
||||
}
|
||||
} else if (entry.getValue() instanceof List) {
|
||||
List<Object> values = (List<Object>) entry.getValue();
|
||||
List<String> vList = new ArrayList<>();
|
||||
List<String> tList = new ArrayList<>();
|
||||
for (Object v : values) {
|
||||
if (v instanceof String) {
|
||||
String val = (String) v;
|
||||
Optional<String> optType = typeList.stream().filter(s -> val.startsWith(s + " ")).findFirst();
|
||||
if (optType.isPresent()) {
|
||||
vList.add(val.substring(optType.get().length() + 1));
|
||||
tList.add(optType.get());
|
||||
} else {
|
||||
vList.add(val);
|
||||
tList.add(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
rs.put(entry.getKey(), vList);
|
||||
types.put(entry.getKey(), tList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,8 +154,46 @@ public class PathMapping {
|
||||
Object starValObj = null;
|
||||
String starEntryKey = null;
|
||||
|
||||
for (Entry<String, String> entry : rs.entrySet()) {
|
||||
String path = entry.getValue();
|
||||
for (Entry<String, Object> entry : rs.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
String path = (String) entry.getValue();
|
||||
String type = (String) types.get(entry.getKey());
|
||||
Object obj = getRefValue(ctxNode, type, path);
|
||||
if (CommonConstants.WILDCARD_STAR.equals(entry.getKey())) {
|
||||
starValObj = obj;
|
||||
starEntryKey = entry.getKey();
|
||||
} else {
|
||||
setByPath(target, entry.getKey(), obj, supportMultiLevels);
|
||||
}
|
||||
} else if (entry.getValue() instanceof List) {
|
||||
List<String> refs = (List<String>) entry.getValue();
|
||||
List<String> tList = (List<String>) types.get(entry.getKey());
|
||||
List<Object> refValList = new ArrayList<>();
|
||||
for (int i = 0; i < refs.size(); i++) {
|
||||
String path = refs.get(i);
|
||||
String type = tList.get(i);
|
||||
Object obj = getRefValue(ctxNode, type, path);
|
||||
// Only header form-data and query Parameter support multiple values, merge result into
|
||||
// one a list
|
||||
if (obj instanceof List) {
|
||||
refValList.addAll((List<Object>) obj);
|
||||
} else {
|
||||
refValList.add(obj);
|
||||
}
|
||||
}
|
||||
setByPath(target, entry.getKey(), refValList, supportMultiLevels);
|
||||
}
|
||||
}
|
||||
|
||||
if(starEntryKey != null) {
|
||||
setByPath(target, starEntryKey, starValObj, supportMultiLevels);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private static Object getRefValue(ONode ctxNode, String type, String path) {
|
||||
Object obj = null;
|
||||
try {
|
||||
String p = path;
|
||||
String defaultValue = null;
|
||||
if (path.indexOf("|") != -1) {
|
||||
@@ -143,15 +201,23 @@ public class PathMapping {
|
||||
defaultValue = path.substring(path.indexOf("|") + 1);
|
||||
}
|
||||
ONode val = select(ctxNode, handlePath(p));
|
||||
|
||||
Object obj = null;
|
||||
if (val != null && !val.isNull()) {
|
||||
obj = val;
|
||||
} else {
|
||||
obj = defaultValue;
|
||||
}
|
||||
if (obj != null && types.containsKey(entry.getKey())) {
|
||||
switch (types.get(entry.getKey())) {
|
||||
if (obj != null && type != null) {
|
||||
obj = cast(obj, type);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new FizzRuntimeException(String.format("path mapping errer: %s , path mapping data: %s %s", e.getMessage(), type, path), e);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static Object cast(Object obj, String type) {
|
||||
switch (type) {
|
||||
case "Integer":
|
||||
case "int": {
|
||||
if (obj instanceof ONode) {
|
||||
@@ -205,20 +271,7 @@ public class PathMapping {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CommonConstants.WILDCARD_STAR.equals(entry.getKey())) {
|
||||
starValObj = obj;
|
||||
starEntryKey = entry.getKey();
|
||||
} else {
|
||||
setByPath(target, entry.getKey(), obj, supportMultiLevels);
|
||||
}
|
||||
}
|
||||
|
||||
if(starEntryKey != null) {
|
||||
setByPath(target, starEntryKey, starValObj, supportMultiLevels);
|
||||
}
|
||||
|
||||
return target;
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static ONode select(ONode ctxNode, String path) {
|
||||
@@ -270,7 +323,14 @@ public class PathMapping {
|
||||
}
|
||||
Map<String, Object> rs = new HashMap<>();
|
||||
for (Entry<String, Object> entry : rules.entrySet()) {
|
||||
if (!(entry.getValue() instanceof String)) {
|
||||
if (entry.getValue() instanceof List) {
|
||||
List<Object> values = (List<Object>) entry.getValue();
|
||||
for (Object v : values) {
|
||||
if (!(v instanceof String)) {
|
||||
rs.put(entry.getKey(), v);
|
||||
}
|
||||
}
|
||||
} else if (!(entry.getValue() instanceof String) && entry.getValue() instanceof Map) {
|
||||
rs.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
@@ -418,6 +478,7 @@ public class PathMapping {
|
||||
*/
|
||||
public static Map<String, Object> transform(ONode ctxNode, StepContext<String, Object> stepContext,
|
||||
Map<String, Object> fixed, Map<String, Object> mappingRules, boolean supportMultiLevels) {
|
||||
try {
|
||||
if (fixed != null && fixed.containsKey(CommonConstants.WILDCARD_TILDE)) {
|
||||
Object val = fixed.get(CommonConstants.WILDCARD_TILDE);
|
||||
fixed = new HashMap<>();
|
||||
@@ -434,15 +495,18 @@ public class PathMapping {
|
||||
}
|
||||
if (mappingRules != null) {
|
||||
// 路径映射
|
||||
ONode target = PathMapping.transform(ctxNode, mappingRules, supportMultiLevels);
|
||||
ONode target = transform(ctxNode, mappingRules, supportMultiLevels);
|
||||
// 脚本转换
|
||||
Map<String, Object> scriptRules = PathMapping.getScriptRules(mappingRules);
|
||||
Map<String, Object> scriptRules = getScriptRules(mappingRules);
|
||||
Map<String, Object> scriptResult = ScriptHelper.executeScripts(target, scriptRules, ctxNode, stepContext, supportMultiLevels);
|
||||
if (scriptResult != null && !scriptResult.isEmpty()) {
|
||||
result = MapUtil.merge(result, scriptResult);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}catch(FizzRuntimeException e) {
|
||||
throw new FizzRuntimeException(e.getMessage(), e, stepContext);
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> convertPath(Map<String, Object> fixed, boolean supportMultiLevels) {
|
||||
|
||||
@@ -143,8 +143,8 @@ public class RequestInput extends RPCInput implements IInput{
|
||||
|
||||
// headers
|
||||
Map<String, Object> headers = PathMapping.transform(ctxNode, stepContext,
|
||||
MapUtil.upperCaseKey((Map<String, Object>) requestMapping.get("fixedHeaders")),
|
||||
MapUtil.upperCaseKey((Map<String, Object>) requestMapping.get("headers")), false);
|
||||
MapUtil.upperCaseKey(MapUtil.list2Map(requestMapping.get("fixedHeaders"))),
|
||||
MapUtil.upperCaseKey(MapUtil.list2Map(requestMapping.get("headers"))), false);
|
||||
if (headers.containsKey(CommonConstants.WILDCARD_TILDE)
|
||||
&& headers.get(CommonConstants.WILDCARD_TILDE) instanceof Map) {
|
||||
request.put("headers", headers.get(CommonConstants.WILDCARD_TILDE));
|
||||
@@ -154,8 +154,8 @@ public class RequestInput extends RPCInput implements IInput{
|
||||
|
||||
// params
|
||||
params.putAll(PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) requestMapping.get("fixedParams"),
|
||||
(Map<String, Object>) requestMapping.get("params"), false));
|
||||
MapUtil.list2Map(requestMapping.get("fixedParams")),
|
||||
MapUtil.list2Map(requestMapping.get("params")), false));
|
||||
if (params.containsKey(CommonConstants.WILDCARD_TILDE)
|
||||
&& params.get(CommonConstants.WILDCARD_TILDE) instanceof Map) {
|
||||
request.put("params", params.get(CommonConstants.WILDCARD_TILDE));
|
||||
@@ -170,8 +170,8 @@ public class RequestInput extends RPCInput implements IInput{
|
||||
supportMultiLevels = false;
|
||||
}
|
||||
Map<String,Object> body = PathMapping.transform(ctxNode, stepContext,
|
||||
(Map<String, Object>) requestMapping.get("fixedBody"),
|
||||
(Map<String, Object>) requestMapping.get("body"), supportMultiLevels);
|
||||
MapUtil.list2Map(requestMapping.get("fixedBody")),
|
||||
MapUtil.list2Map(requestMapping.get("body")), supportMultiLevels);
|
||||
if (body.containsKey(CommonConstants.WILDCARD_TILDE)) {
|
||||
request.put("body", body.get(CommonConstants.WILDCARD_TILDE));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user