java - 如何解决 spring-boot 中的 URI 编码问题?
问题描述
我正在使用 spring-boot 来托管 http 请求服务。
@RequestMapping("/extract")
@SuppressWarnings("unchecked")
@ResponseBody
public ExtractionResponse extract(@RequestParam(value = "extractionInput") String input) {
// LOGGER.info("input: " + input);
JSONObject inputObject = JSON.parseObject(input);
InputInfo inputInfo = new InputInfo();
//Object object = inputObject.get(InputInfo.INPUT_INFO);
JSONObject object = (JSONObject) inputObject.get(InputInfo.INPUT_INFO);
String inputText = object.getString(InputInfo.INPUT_TEXT);
inputInfo.setInputText(inputText);
return jnService.getExtraction(inputInfo);
}
当有%符号时,如下所示,报错:
http://localhost:8090/extract?extractionInput={"inputInfo":{"inputText":"5.00%"}}
错误信息如下:
2018-10-09 at 19:12:53.340 [http-nio-8090-exec-1] INFO org.apache.juli.logging.DirectJDKLog [180] [log] - Character decoding failed. Parameter [extractionInput] with value [{"inputInfo":{"inputText":"5.0022:%225.00%%22}}] has been ignored. Note that the name and value quoted here may be corrupted due to the failed decoding. Use debug level logging to see the original, non-corrupted values.
Note: further occurrences of Parameter errors will be logged at DEBUG level.
2018-10-09 at 19:12:53.343 [http-nio-8090-exec-1] WARN org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver [140] [resolveException] - Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'extractionInput' is not present]
如何在我的 spring-boot 配置中配置 URI 编码来解决这个问题?
编辑:发出请求的可能 Java 客户端代码:
public String process(String question) {
QueryInfo queryInfo = getQueryInfo(question);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String jsonResult = null;
try {
String jsonStr = mapper.writeValueAsString(queryInfo);
String urlStr = Parameters.getQeWebserviceUrl() + URLEncoder.encode(jsonStr, "UTF-8");
URL url = new URL(urlStr);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
jsonResult = in.readLine();
in.close();
} catch (Exception jpe) {
jpe.printStackTrace();
}
return jsonResult
}
解决方案
如果您的客户端没有编码 - 如果您在 servlet 中处理请求之前遵循以下任何策略通过编码来实现此目的:
- 使用 Spring 预处理器 bean 预处理控制器端点请求
- 使用 Spring AspectJ 预处理控制器端点请求
- 使用 Spring servlet 过滤器预处理控制器端点请求
使用上述任何横切策略,您都可以对请求 URL 进行编码并传回端点。
例如下面是使用过滤器的一种实现。如果您需要更好的性能,您可以在那里进行一些缓存。
@Component
public class SomeFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeFilter.class);
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletRequest modifiedRequest = new SomeHttpServletRequest(request);
filterChain.doFilter(modifiedRequest, servletResponse);
}
@Override
public void destroy() {
}
class SomeHttpServletRequest extends HttpServletRequestWrapper {
HttpServletRequest request;
SomeHttpServletRequest(final HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getQueryString() {
String queryString = request.getQueryString();
LOGGER.info("Original query string: " + queryString);
try {
// You need to escape all your non encoded special characters here
String specialChar = URLEncoder.encode("%", "UTF-8");
queryString = queryString.replaceAll("\\%\\%", specialChar + "%");
String decoded = URLDecoder.decode(queryString, "UTF-8");
LOGGER.info("Modified query string: " + decoded);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return queryString;
}
@Override
public String getParameter(final String name) {
String[] params = getParameterMap().get(name);
return params.length > 0 ? params[0] : null;
}
@Override
public Map<String, String[]> getParameterMap() {
String queryString = getQueryString();
return getParamsFromQueryString(queryString);
}
@Override
public Enumeration<String> getParameterNames() {
return Collections.enumeration(getParameterMap().keySet());
}
@Override
public String[] getParameterValues(final String name) {
return getParameterMap().get(name);
}
private Map<String, String[]> getParamsFromQueryString(final String queryString) {
String decoded = "";
try {
decoded = URLDecoder.decode(queryString, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String[] params = decoded.split("&");
Map<String, List<String>> collect = Stream.of(params)
.map(x -> x.split("="))
.collect(Collectors.groupingBy(
x -> x[0],
Collectors.mapping(
x -> x.length > 1 ? x[1] : null,
Collectors.toList())));
Map<String, String[]> result = collect.entrySet().stream()
.collect(Collectors.toMap(
x -> x.getKey(),
x -> x.getValue()
.stream()
.toArray(String[]::new)));
return result;
}
}
}
推荐阅读
- blockchain - 确认比特币交易
- python - 比较两列后 Pandas 计数器递增
- vb.net - 在客户机器上部署带有水晶报表的 vb.net 2017 应用程序
- algorithm - 查找 2 个多边形之间的碰撞
- c++ - 如何在 c++ 中将`this`传递给没有循环引用(或丢失类型)的构造函数?
- c++ - Box2D 对象位置不受速度变化的影响
- sql - 如何检查一列是否在 PostgreSQL 中具有所有增加的值
- python - AttributeError:“张量”对象没有“编译”属性
- javascript - Whack-a-mole 无法点击新图片
- node.js - users 不是 graphql 中的构造函数