简体   繁体   English

在播放框架 2.5 中使用 WSClient 将文件和 JSON 正文一起发布

[英]Post file and JSON body together using WSClient in play framework 2.5

I am trying to post multipart form data (file and json) to a third party client using WSClient in Play framework.我正在尝试使用Play框架中的WSClient将多部分表单数据(文件和 json)发布到第三方客户端。

Source<ByteString, ?> file = FileIO.fromFile(new File("hello.txt"));
FilePart<Source<ByteString, ?>> fp = new FilePart<>("hello", "hello.txt", "text/plain", file);
DataPart dp = new DataPart("key", "value");

ws.url(url).post(Source.from(Arrays.asList(fp, dp)));

As per their docs they mentioned to send it as above.根据他们的文档,他们提到要按上述方式发送。

I keep getting error as bad request.我不断收到错误请求。 It seems request is not formed correctly.似乎请求没有正确形成。 Could someone explain how can it be done?有人可以解释一下怎么做吗?

This is what I get back in response这是我得到的回应

    NettyResponse {
    statusCode=400
    headers=
        Cache-Control: must-revalidate,no-cache,no-store
        Content-Type: text/html; charset=ISO-8859-1
        Date: Mon, 30 Jan 2017 15:33:20 GMT
        Content-Length: 310
        Connection: keep-alive
    body=
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Error 400 Bad Request</title>
</head>
<body><h2>HTTP ERROR 400</h2>
<p>Problem accessing /client/document/upload. Reason:
<pre>    Bad Request</pre></p><hr><i><small>Powered by Jetty://</small></i><hr/>

</body>
</html>

}

I have this class that you can use to forward either Multipart Request or Nornmal Json.我有这个类,您可以使用它来转发 Multipart Request 或 Nornmal Json。 Adapt to your use case.适应您的用例。 Extend the class in your controller, and call return this.forwardRequest(request());扩展控制器中的类,并调用 return this.forwardRequest(request()); or return forwardMultipartRequest(request());或返回 forwardMultipartRequest(request());

import akka.stream.javadsl.FileIO;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import play.mvc.Controller;
import play.mvc.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.commons.lang3.tuple.Pair;
import play.libs.ws.WSClient;
import java.util.Objects;
import org.apache.commons.lang.StringUtils;
import play.libs.ws.WSRequest;
import play.mvc.Http.MultipartFormData.*;

/**
 *
 * @author poseidon
 */
public class RequestMaker extends Controller {

    @Inject
    WSClient wSClient;

    protected CompletionStage<Result> forwardRequest(Http.Request request) {

       return forwardRequest(request, false);
    }

    protected CompletionStage<Result> forwardMultipartRequest(Http.Request request) {
        //select only needed headers
        Map<String, List<String>> headers = request.headers().entrySet().stream().filter(g -> g.getKey().equalsIgnoreCase("authorization")).map(f -> Pair.of(f.getKey(), Arrays.asList(f.getValue()))).collect(Collectors.toMap(g -> g.getLeft(), g -> g.getRight()));
        Map<String, List<String>> queryParams = request.queryString().entrySet().stream().map(f -> Pair.of(f.getKey(), Arrays.asList(f.getValue()))).collect(Collectors.toMap(g -> g.getLeft(), g -> g.getRight()));
        String path = request.path();
        WSRequest wSRequest = wSClient.url(String.format("%s%s", System.getenv("API_SERVER_URL"), request.path())).setHeaders(headers).setQueryString(queryParams);

        Http.MultipartFormData<java.io.File> asMultipartFormData = request.body().asMultipartFormData();
        List<Http.MultipartFormData.Part<Source<ByteString, ?>>> files = asMultipartFormData.getFiles().stream().map(g -> {
            FilePart<Source<ByteString, ?>> fp = new FilePart<>(g.getKey(), g.getFilename(), g.getContentType(), FileIO.fromFile(g.getFile()));
            return fp;
        }).collect(Collectors.toList());
        return wSRequest.post(Source.from(files)).thenApply(fn -> {
            if (StringUtils.isNotBlank(fn.getBody())) {
                return status(fn.getStatus(), fn.getBody()).as(fn.getContentType());
            }
            return status(fn.getStatus()).as(fn.getContentType());
        }).exceptionally(fn -> {
            play.Logger.error("Failed to call path = {}, headers = {}, query = {}", path, headers, queryParams, fn);
            return badRequest(fn.getMessage());
        });
    }

    protected CompletionStage<Result> forwardRequestWithByteResponse(Http.Request request) {

      return forwardRequest(request, true);
    }

    protected CompletionStage<Result> forwardRequest(Http.Request request, boolean byteResponse) {

        Map<String, List<String>> headers = request.headers().entrySet().stream().map(f -> Pair.of(f.getKey(), Arrays.asList(f.getValue()))).collect(Collectors.toMap(g -> g.getLeft(), g -> g.getRight()));
        Map<String, List<String>> queryParams = request.queryString().entrySet().stream().map(f -> Pair.of(f.getKey(), Arrays.asList(f.getValue()))).collect(Collectors.toMap(g -> g.getLeft(), g -> g.getRight()));
        String path = request.path();
        WSRequest wSRequest = wSClient.url(String.format("%s%s", System.getenv("API_SERVER_URL"), request.path())).setHeaders(headers).setQueryString(queryParams);
        if (request.hasBody() && Objects.nonNull(request.body().asJson())) {
            wSRequest = wSRequest.setBody(request.body().asJson());
        }
        return wSRequest.setMethod(request.method()).execute().thenApply(fn -> {
            if (byteResponse && Objects.nonNull(fn.asByteArray())) {
                return status(fn.getStatus(), fn.asByteArray()).as(fn.getContentType());
            }
            if (StringUtils.isNotBlank(fn.getBody())) {
                return status(fn.getStatus(), fn.getBody()).as(fn.getContentType());
            }
            return status(fn.getStatus()).as(fn.getContentType());
        }).exceptionally(fn -> {
            play.Logger.error("Failed to call path = {}, headers = {}, query = {}", path, headers, queryParams, fn);
            return badRequest(fn.getMessage());
        });
    }
}

It is late but might help some one.为时已晚,但可能会对某些人有所帮助。 This might be because the server is not accepting chinked-transfer.这可能是因为服务器不接受 chinked-transfer。 In play framework, based on the code in, https://github.com/playframework/playframework/blob/2.5.x/framework/src/play-java-ws/src/main/java/play/libs/ws/ahc/AhcWSRequest.java#L520-L533在play框架中,基于https://github.com/playframework/playframework/blob/2.5.x/framework/src/play-java-ws/src/main/java/play/libs/ws/中的代码ahc/AhcWSRequest.java#L520-L533

// If the body has a streaming interface it should be up to the user to provide a manual Content-Length // 如果正文有流接口,则应由用户提供手动 Content-Length

// else every content would be Transfer-Encoding: chunked //否则每个内容都将被传输编码:分块

// If the Content-Length is -1 Async-Http-Client sets a Transfer-Encoding: chunked // 如果 Content-Length 为 -1 Async-Http-Client 设置 Transfer-Encoding: chunked

// If the Content-Length is great than -1 Async-Http-Client will use the correct Content-Length // 如果 Content-Length 大于 -1 Async-Http-Client 将使用正确的 Content-Length

I faced the same issue, adding Content-Length header solved in mycase.我遇到了同样的问题,添加了在 mycase 中解决的 Content-Length 标头。 Added header as below to WSRequest,向 WSRequest 添加如下标头,

wsRequest.setHeader("Content-Length", String.valueOf(fileToUpload.length()));

Here fileToUpload is java.io.File object which you are trying to upload.这里 fileToUpload 是您尝试上传的 java.io.File 对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM