【问题标题】:Spring Boot RestTemplate exchange 400 bad requestSpring Boot RestTemplate 交换 400 错误请求
【发布时间】:2019-03-20 16:41:51
【问题描述】:

Spring Boot RestTemplate 交换有问题。

我有以下代码:

@RequestMapping(path = "/add")
public @ResponseBody String addFromTo () {
    String apikey = "";
    String baseurl = "http://demowebshop.webshop8.dk/admin/WEBAPI/v2/orders?start=2018-10-05T20%3A49%3A41.745Z&end=2018-10-15T20%3A49%3A41.745Z&api_key=" + apikey;

    RestTemplate restTemplate = new RestTemplate();

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.setBasicAuth("", apikey);

    HttpEntity<String> request = new HttpEntity<String>(" ", headers);
    ResponseEntity<OrderResponse> response = restTemplate.exchange(baseurl, HttpMethod.GET, request, OrderResponse.class);

    return "Some text.";
}

我想要的是相当于:

curl -X GET --header 'Accept: application/json' --header 'Authorization: Basic {encodedapikey}' 'http://demowebshop.webshop8.dk/admin/WEBAPI/v2/orders?start=2018-10-06T06%3A43%3A40.926Z&end=2018-10-16T06%3A43%3A40.926Z&api_key={apikey}'

我尝试使用具有完全相同 URL 的 Postman,并使用 apikey 和“Accept: application/json”标头添加基本身份验证,效果很好,但是当我运行此代码时,我得到了错误留言:

There was an unexpected error (type=Internal Server Error, status=500).
400 Bad Request

编辑: Pastebin 链接到程序抛出的异常: https://pastebin.com/jdYJ2nv7

【问题讨论】:

  • setBasicAuth 应该是'' " ?
  • 应该没有用户名。如果您尝试访问 URL,它也可以在浏览器中使用,它会询问用户名和密码,我将用户名留空并使用 apikey 作为密码。
  • 似乎缺少一些东西..如果你看到代码 String apikey = "";但是你在哪里设置这个字符串值..它总是传递为“''。
  • apikey 字段通常不会丢失,我只是没有将其包含在问题中。
  • 您发布的内容不同...参数再次编码,导致%3A 再次编码为其他内容。除此之外,您没有像使用 CURL 那样提供encodedapikey。所以你没有做同样的事情,但期待同样的结果......

标签: java spring rest http spring-boot


【解决方案1】:

HttpHeaders.setBasicAuth(String, String) 仅用于用户名和密码,不用于基本令牌。 如果您想使用基本令牌,请尝试使用 headers.add("Authorization", "Basic " + apiKey) 代替 headers.setBasicAuth(...)

【讨论】:

    【解决方案2】:

    在您的 curl 请求中,您使用的是 apikeyencodedapikey。而在你的 Java 代码中你没有。接下来,您还将传递一个编码的 URL 作为要使用的 URL。这将导致再次编码编码的 URL。所以不要那样做。而是使用带有占位符的 URL 并为它们提供值。

    @RequestMapping(path = "/add")
    public @ResponseBody String addFromTo () {
    
        String apikey = "";
        String baseurl = "http://demowebshop.webshop8.dk/admin/WEBAPI/v2/orders?start={start}&end={end}&api_key={apikey}";
    
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("start", "2018-10-05T20:49:41.745Z");
        parameters.put("end", "2018-10-16T06:43:40.926Z");
        parameters.put("apikey", apikey);
    
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.setBasicAuth("", apikey);
    
        ResponseEntity<OrderResponse> response = restTemplate.getForEntity(baseurl, OrderResponse.class, parameters);
    
        return "Some text.";
    }
    

    上面的代码使用了一个适当的参数化 URL 以及一个包含占位符值的映射。请注意,这些没有编码,因为它将由 Spring! 处理。最后,您可以简单地使用getForEntity 方法而不是exchange 方法来获取结果。

    最后的建议是,Spring Boot 已经配置了一个RestTemplate,您可以(重新)使用它。您不需要在每次需要时都创建RestTemplate(创建它是一个相当繁重的对象,并且创建后它是线程安全的,因此拥有一个实例就足够了)。

    public YourClassCOnstructor(RestTemplateBuilder builder) {
        this.restTemplate = builder.basicAuthorization("", apikey).build();
    }
    

    当然,您也可以将其放入 @Bean 方法中,并将特定的 RestTemplate 注入您的类中。

    【讨论】:

    • 很好的答案,但是如果我使用 getForEntity 而不是交换,我如何包含标题?
    • 我建议使用RestTemplateBuilder 创建RestTemplate(一次!)并在其中设置身份验证,而不是在方法中每次都设置。这样你就不需要设置标题了。
    猜你喜欢
    • 2019-05-19
    • 1970-01-01
    • 2018-06-28
    • 2017-01-22
    • 1970-01-01
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    • 2014-12-28
    相关资源
    最近更新 更多