我正在尝试使用WSClient
框架中的Play
将多部分表单数据(文件和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)));
按照他们提到的docs发送,如上所述。
我一直收到错误请求。似乎请求没有正确形成。有人可以解释一下如何做到这一点?
这是我回复的内容
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>
}
答案 0 :(得分:0)
我有一个此类,您可以用来转发Multipart Request或Nornmal Json。适应您的用例。在您的控制器中扩展该类,并调用return this.forwardRequest(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());
});
}
}
答案 1 :(得分:0)
已经晚了,但可能会帮助一些人。 这可能是因为服务器不接受加密传输。 在播放框架中,基于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
中的代码//如果主体具有流接口,则应由用户自行提供手动的内容长度
//否则每个内容都将进行传输编码:分块
//如果Content-Length为-1,则Async-Http-Client设置了Transfer-Encoding:分块
//如果Content-Length大于-1,则Async-Http-Client将使用正确的Content-Length
我遇到了同样的问题,添加了在mycase中解决的Content-Length标头。 向WSRequest添加了以下标题,
wsRequest.setHeader("Content-Length", String.valueOf(fileToUpload.length()));
这里的fileToUpload是您要上传的java.io.File对象。