When my Java web application receives an HTTP POST request, it needs to create a new OkHttp3 Request from the HttpServletRequest and send this to another URL. The original post request could be simple form data or multi-part.
Here's the interface that I am looking to implement:
import okhttp3.Request;
public interface OkHttp3RequestBuilder {
Request create(HttpServletRequest request);
}
Looks like the challenge boils down to how I would create an okhttp3.RequestBody. Here's the relevant part of the implementation...
final HttpUrl targetUrl = HttpUrl.get("http://internal.xyz.com");
final RequestBody requestBody = // ?????
final Request httpRequest = new Request.Builder()
.post(requestBody)
.url(targetUrl)
.build();
return httpRequest;
How do I go about doing it? Any suggestions? Thanks!
This should work, but request.getReader() must never have been called as you can call it only once.
Request create(HttpServletRequest request)
{
final HttpUrl targetUrl = HttpUrl.get("http://internal.xyz.com");
final String originalBody = request.getReader().lines().collect (Collectors.joining (System.lineSeparator()));
final RequestBody requestBody = RequestBody.create(MediaType.parse(request.getContentType()), originalBody);
final Request httpRequest = new Request.Builder()
.post(requestBody)
.url(targetUrl)
.build();
return httpRequest;
}
Thanks for your answer, ETL! This seems to work for me:
#Override
public RequestBody getRequestBody(final HttpServletRequest httpServletRequest) throws IOException {
final InputStream inputStream = httpServletRequest.getInputStream();
final byte[] requestBodyBytes = ByteStreams.toByteArray(inputStream);
final String contentType = httpServletRequest.getContentType();
final MediaType mediaType = MediaType.parse(contentType);
final RequestBody requestBody = RequestBody.create(mediaType, requestBodyBytes);
return requestBody;
}
Related
I am using java.net.http.HttpClient in my Java Spring Boot app and I noticed this weird behaviour.
When my code call HTTP request to 3rd party API, next request to different 3rd party API returns always as bad request (400). When I execute this request first, it works just fine.
When I restart the app, first API call is always successful, but second one is always bad and so I have to call it again after some timeout and then it work.
So I was thinking, if there is any form of "cache" that remember previous settings or whatever from previous request, because second request to different API is always bad. When I inspected HttpRequest in debugger, it seems okay to me and there was nothing really different from the one that worked.
Here is my bean config
#Configuration
public class HttpClientBean {
#Bean
public HttpClient httpClient() {
return HttpClient.newHttpClient();
}
}
HttpRequest builder
public static HttpRequest buildGetRequest(final String url) {
return HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
}
public static HttpRequest buildPostRequest(final String url, final String body) {
return HttpRequest.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.uri(URI.create(url))
.setHeader(CONTENT_TYPE, APPLICATION_JSON)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
}
and here is HttpService
#Service
public class HttpServiceImpl implements HttpService {
private final HttpClient httpClient;
#Autowired
public HttpServiceImpl(final HttpClient httpClient) {
this.httpClient = httpClient;
}
#Override
public HttpResponse<String> sendGetRequestWithParams(final String url, final String params) throws Exception {
final HttpRequest request = buildGetRequest(url, params);
return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
#Override
public HttpResponse<String> sendGetRequestWithoutParams(final String url) throws Exception {
final HttpRequest request = buildGetRequest(url);
return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
#Override
public HttpResponse<String> sendPostRequestWithBody(final String url, final String body) throws Exception {
final HttpRequest request = buildPostRequest(url, body);
return httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}
}
Thank you for your advices.
I am add a interpreter in retrofit like this:
public static Retrofit InitRetrofitOkhttp(String configKey) {
String tenantId = MyContext.getCurrentTenantId() == null ? "" : MyContext.getCurrentTenantId().toString();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
builder.writeTimeout(20, TimeUnit.SECONDS);
builder.retryOnConnectionFailure(true);
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header(MyContext.VERIFY_TENANT_ID, tenantId);
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
Config config = ConfigService.getAppConfig();
String baseUrl = config.getProperty(configKey, "127.0.0.1");
OkHttpClient client = builder.build();
Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
retrofitBuilder.client(client);
retrofitBuilder.baseUrl(baseUrl);
retrofitBuilder.addConverterFactory(GsonConverterFactory.create());
Retrofit sRetrofit = retrofitBuilder.build();
return sRetrofit;
}
what I want to do is add different tenant_id in every http request, but it seems only the first time initial retrofit add interpreter(I could not debbugging in interpreter), what should I do to make it work? I did not know I do like this works.
Move
String tenantId = MyContext.getCurrentTenantId() == null ? "" : MyContext.getCurrentTenantId().toString();
into intercept method
When sending POST requests to an API endpoint using #FormUrlEncoded from RetroFit2, the record looked scrambled when it was Japanese but ok when English.
The charset was set to null when using to send POST request.
Content-Type:application/x-www-form-urlencoded was the header instead of Content-Type:application/x-www-form-urlencoded; charset=utf-8
#FormUrlEncoded
#POST("/api/items")
Call<ApiResponse> post(#FieldMap Map<String, Object> fields);
2 Solutions:
add the headers manually for each endpoint, not very scaleable solution
#FormUrlEncoded
#POST("/api/items")
#Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Call<ApiResponse> post(#FieldMap Map<String, Object> fields);
add an interceptor to add it for endpoints having POST, a bit more verbose:
#Bean
public ApiService apiService(final ObjectMapper objectMapper) {
final OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(chain -> {
final Request original = chain.request();
if (original.method().equals("POST")) {
final Buffer buffer = new Buffer();
original.body().writeTo(buffer);
final String content = buffer.readString(StandardCharsets.UTF_8);
final RequestBody body = RequestBody.create(MediaType.parse(""application/x-www-form-urlencoded"), content);
final Request request = original.newBuilder().method(original.method(), body).build();
return chain.proceed(request);
} else {
return chain.proceed(original);
}
}).build();
final Retrofit retrofit = new Retrofit.Builder()
.client(httpClient)
.baseUrl(apiBaseUrl)
.build();
return retrofit.create(ApiService.class);
}
I would like to use a global header for all my requests. Therefore I have implemented the following class:
public class HeaderInterceptor {
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.method("GET", null)
.addHeader("Accept", "application/json")
.addHeader("Basic ", "abcdefghi123456789")
.build();
Response response = chain.proceed(request);
return response;
}
}
Now I would like to do the following in the main()-method:
public static void main(String[] args) throws Exception {
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(MyInterceptor).build();
Request reqAllProjects = new Request.Builder()
.url("https://example.com/projects")
.build();
Response resAllProjects = httpClient.newCall(reqAllProjects).execute();
String responseData = resAllProjects.body().string();
System.out.println(responseData);
}
I'm not sure now how to use my HeaderInterceptor. I guess I'll have to enter it here, right?
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(??MyInterceptor??).build();
I tried something like this: addInterceptor(HeaderInterceptor.intercept()) but this is not working...
Can someone help me please? And does the rest of it look fine? Many thanks in advance!
The interceptor class that you have created doesn't seem to be implementing the Interceptor interface. You need to implement as below
public class HeaderInterceptor implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Basic ", "abcdefghi123456789")
.build();
Response response = chain.proceed(request);
return response;
}
}
Do note that you should not be modifying the method and body of the request as .method("GET", null) unless you actually need so, as it can result in all the HTTP requests made by the client to make GET requests with null body.
Then add the interceptor while building the client as below
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new HeaderInterceptor()).build();
Have a look at the OkHttp documentation for more info.
Have you checked this question : Okhttp3: Add global header to all requests error
It should be something like
.addInterceptor(new Interceptor())
I'm facing a different Api Service which I have to request using POST but with no body content, I'm sending a image converted to base64, I've been searching about that issue and I found this "solution", which it didn't work :
1 :
RequestBody reqbody = RequestBody.create(null, new byte[0]);
Request.Builder formBody = new Request.Builder().url(url).method("POST",reqbody).header("Content-Length", "0");
2 :
request = new Request.Builder()
.url(BASE_URL + route)
.method("POST", RequestBody.create(null, new byte[0]))
.post(requestBody)
.build();
Even I explicit saying that is a POST method, it keeps send a GET request and not a POST request. Thanks!
My Activity :
public String SendImage(String image64) throws IOException{
//RequestBody reqbody = RequestBody.create(null, new byte[0]);
Request request = new Request.Builder()
.url("http://ap.imagensbrasil.org/api/1/upload/?key=9c9dfe77cd3bdbaa7220c6bbaf7452e7&source=" + image64 + "&format=txt")
.method("POST", RequestBody.create(null, new byte[0]))
.header("Content-Length", "0")
.build();
OkHttpClient Client = client.newBuilder() .readTimeout(25, TimeUnit.SECONDS).build();
Response response = Client.newCall(request).execute();
return response.body().string();
}
It is working on retrofit , so if you continue with Retrofit v2.0 you can use this :
public class Base64EncodeRequestInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder();
if (originalRequest.method().equalsIgnoreCase(POST)) {
builder = originalRequest.newBuilder()
.method(originalRequest.method(), encode(originalRequest.body()));
}
return chain.proceed(builder.build());
}
private RequestBody encode(RequestBody body) {
return new RequestBody() {
#Override
public MediaType contentType() {
return body.contentType();
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
Buffer buffer = new Buffer();
body.writeTo(buffer);
byte[] encoded = Base64.encode(buffer.readByteArray(), Base64.DEFAULT);
sink.write(encoded);
buffer.close();
sink.close();
}
};
}
}