【问题标题】:How to resolve URI encoding problem in spring-boot?如何解决 spring-boot 中的 URI 编码问题?
【发布时间】:2018-10-10 02:17:12
【问题描述】:

我正在使用 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
}

【问题讨论】:

标签: java spring-boot


【解决方案1】:

如果您的客户端没有编码 - 如果您在 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;
        }
    }
}

【讨论】:

  • 你能举一个例子来实现这一点吗?对春天不太熟悉。
  • 见上面我的 cmets。
  • 鉴于我的情况,如何编写这样的拦截器来解决这个问题?我认为这应该是一个非常普遍的需求。
  • 你能看看stackoverflow.com/questions/52746082/…吗?我跟着一个拦截器。
  • 太好了!镇阳。
【解决方案2】:

您可能需要对查询参数进行 URLEncode,例如

http://localhost:8090/extract?extractionInput=%7B%22inputInfo%22%3A%7B%22inputText%22%3A%225.00%25%22%7D%7D

像这样传递参数的通常更简单的方法是使用 HTTP POST 而不是 GET,并在正文中传递您的 JSON 对象。

【讨论】:

  • 有没有办法在 spring-boot 配置中进行配置来解决这个问题?我做了一些搜索并尝试了一些配置选项,但都没有解决。
  • 不,如果您使用 GET 和查询参数,您需要创建完整的请求 URL,例如:"http://localhost:8090/extract?extractionInput=" + URLEncoder.encode("{\"inputInfo\":{\"inputText\":\"5.00%\"}}", "UTF-8");"
  • @GreyBearedGeek,您能否根据我发布的代码给出 POST 请求的版本?我以前从未使用过 POST 请求。
  • 您没有发布任何发出请求的代码 - 我不知道您使用什么语言发出请求,更不用说什么库等。如果您发布发出 GET 的代码请求,我可能可以提供帮助。
  • 请查看我的更新。这是在 Java 中发出请求的方式,它也可以在 Python 代码中使用。但我希望它工作的第一件事就是在浏览器中获得正确的结果,而不是在任何代码中。目前,它使服务崩溃。
【解决方案3】:

这不是 REST API 的最佳做法。 尝试以面向对象的方式规范化您的 URL 以捕获路径变量。

如果你的对象喜欢:

 param1:{ 
   param2:{ 
     param3: ""
          }
        }

使用 url 模式将属性捕获为:

class/param1/param2/{param3}

否则,在更改前端技术同时保持后端 REST API 不变时,您会遇到更多问题。

【讨论】:

  • 没听懂。您能否通过示例“localhost:8090/extract?extractionInput={"inputInfo":{"inputText":"5.00%"}}" 使其更具体?假设“InputInfo”只有两个字段“inputText”和“id”。
  • “使用 url 模式捕获属性为:”是什么意思?
猜你喜欢
  • 2020-01-06
  • 2011-03-29
  • 2021-04-16
  • 2021-11-14
  • 1970-01-01
  • 2017-10-19
  • 2019-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多