How to add parameters to java.net.http.HttpClient GET request? - java

We found the following example, which works:
import java.net.http.HttpClient;
:
private static final HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(TIMEOUT)).build();
:
public String getStuff(HashMap<String,String> params) {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create("https://httpbin.org/get"))
.setHeader("User-Agent", "My Agent v1.0")
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return response.body();
}
The question is how do we get the params into the request? We could manually put them in the URI by string manipulation, but this wont work for POST.
We would expect a setParameter method similar to setHeader, but this doesnt exist (according to eclipse at least).
For now I am doing it manually thusly:
String uri = "http://Somesite.com/somepath";
if (params != null) {
uri += "?";
for (String key : params.keySet()) {
uri += "" + key + "=" + params.get(key);
}
}
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(uri))
.setHeader("User-Agent", agent)
.build();
Presumably for POST ill have to manually construct a body with the post params structure.

Use javax.ws.rs.core.UriBuilder
It has queryParam method.
e.g.:
UriBuilder.fromLink( Link.fromUri( "somehost" ).build() )
.path( API_SERVICES )
.queryParam( "path", path)
.queryParam( "method", method )
.build();

Related

how to pass tenant_id in post mapping URL

Have following URL in postman to get the access token.
https://login.microsoftonline.com/:tenant_id/oauth2/token
now writing java code to do the same thing which I am doing in postman.
here is the sample code
public class RequestTest {
public static void main(String[] args) throws IOException, InterruptedException {
// TODO Auto-generated method stub
String tenant_id = "<tenant_id>";
String keys = "<client_id>:<client_secret>";
String url = "https://login.microsoftonline.com/:" + tenant_id + "/oauth2/token";
HashMap<String, String> parameters = new HashMap<>();
parameters.put("grant_type", "client_credentials");
String form = parameters.keySet().stream().map(key -> key + "=" + URLEncoder.encode(parameters.get(key),StandardCharsets.UTF_8)).collect(Collectors.joining("&"));
String encoding = Base64.getEncoder().encodeToString(keys.getBytes());
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url))
.headers("Content-Type", "application/x-www-form-urlencoded", "Authorization", "Basic "+encoding)
.POST(BodyPublishers.ofString(form)).build();
HttpResponse<?> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode() + response.body().toString());
}
}
but somehow the URL is not getting formed properly as i am seeing following error :
400{"error":"invalid_request","error_description":"AADSTS900023:
Specified tenant identifier ':tenant_id' is
neither a valid DNS name, nor a valid external domain.\r\nTrace ID:
652e1996-1863-4183-aac5-ee9a74680600\r\nCorrelation ID:
45396fd8-ee9c-423b-ae5d-3bf8885d4532\r\nTimestamp: 2021-04-15
09:24:51Z","error_codes":[900023],"timestamp":"2021-04-15
09:24:51Z","trace_id":"652e1996-1863-4183-aac5-ee9a74680600","correlation_id":"45396fd8-ee9c-423b-ae5d-3bf8885d4532","error_uri":"https://login.microsoftonline.com/error?code=900023"}
please suggest where i am missing in the code ? any references please . Thanks
The request url is wrong, just remove : from it, it should be
https://login.microsoftonline.com/" + tenant_id + "/oauth2/token

How to upload files using JDK 11 java.net.http.HttpClient?

I recently encountered some problems with java.net.http.HttpClient that comes with JDK 11. I don't know how to use file upload. Found the ofInputStream() in java.net.http.BodyPublishers. I don't know if I using this method file upload.
Here are the examples I wrote.
public HttpResponse<String> post(String url, Supplier<? extends InputStream> streamSupplier, String... headers) throws IOException, InterruptedException {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(URI.create(url))
.headers(headers)
.POST(null == streamSupplier ?
HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofInputStream(streamSupplier));
HttpRequest request = builder.build();
log.debug("Execute HttpClient Method:『{}』, Url:『{}』", request.method(), request.uri().toString());
return client.send(request, HttpResponse.BodyHandlers.ofString());
}
The HttpRequest type provide factory method for creating request publisher for handling body type such as file:
HttpRequest.BodyPublishers::ofFile(Path)
You can update your method:
public HttpResponse<String> post(String url, Path file, String... headers) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.headers(headers)
.POST(null == file ? HttpRequest.BodyPublishers.noBody() :
HttpRequest.BodyPublishers.ofFile(file))
.build();
log.debug("Execute HttpClient Method:『{}』, Url:『{}』", request.method(),
request.uri().toString());
return client.send(request, HttpResponse.BodyHandlers.ofString());
}
The java.net.http.HttpClient handles bytes supplied through the BodyPublisher as raw body data, without any interpretation. Whether you use HttpRequest.BodyPublishers::ofFile(Path) or HttpRequest.BodyPublishers::ofByteArray(byte[]) is therefore semantically irrelevant: what changes is simply how the bytes that will be transmitted are obtained.
In case of file upload - your server probably expects that the request body will be formatted in certain ways. It might also expect some specific headers to be transmitted with the request (such as Content-Type etc). The HttpClient will not do that magically for you. This is something you need to implement at the caller level.
you may use the method by:
public void uploadLocalFileToRemote(String notEncodedUrlStr, String remoteFilename, String localSourceDir, String localFilename) {
Path sourcePath = Path.of(localSourceDir, localFilename);
if(!sourcePath.toFile().canRead())
{
System.err.println("please check the local file existance/readability: " + sourcePath.toAbsolutePath());
return;
}
FileInputStream ins = null;
try {
ins = new FileInputStream(sourcePath.toFile());//FileNotFoundException extends IOException
BufferedInputStream buf_ins = new BufferedInputStream(ins);
Supplier<? extends InputStream> streamSupplier = new Supplier<BufferedInputStream>() {
#Override
public BufferedInputStream get() {
return buf_ins;
}
};
//HttpResponse<String> response = post(notEncodedUrlStr, streamSupplier,
HttpResponse<String> response = post(notEncodedUrlStr, () -> buf_ins,
"User-Agent", "Java 11 HttpClient Bot", "Content-type", "application/octet-stream",
"accept", "*/*", "fileName", remoteFilename);
// print response:
System.out.println(response.version().name() + " " + response.statusCode());
// print response headers
HttpHeaders headers = response.headers();
headers.map().forEach((k, v) -> System.out.println(k + ":" + v));
// print response body
String body = response.body();
System.out.println(body);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
another consideration is how your server side is implemented. here assume the server side will using http 1.1 "chunked". and configured a directory for remoteFilename.

Requesting Twitter api with OAuth 1.0

I'm trying to use Twitter's friends list api and was successful to do so without any parameters.
However whenever I add a parameter, I would get the error "Could not authenticate you." and I have no choice but to add a cursor parameter when the friend list is too long.
The fact that I get a list of users of friends when I call the api without any parameters makes me think that authenticating the request works properly.
I have tried to change the request url to https://api.twitter.com/1.1/friends/list.json?cursor=-1 which gives me the authentication error.
I tried using both https://api.twitter.com/1.1/friends/list.json and https://api.twitter.com/1.1/friends/list.json?cursor=-1 to make oauth_signature and they both failed me.
I tried using different parameters such as screen_name or user_id and they all will give me the same error.
I even tried to add cursor: -1 header like a POST request and that didn't work either.
Right now my code looks like this
public String getFriendList() {
String baseUrl = "https://api.twitter.com/1.1/friends/list.json";
// Creates a map with all necessary headers
Map<String, String> headers = createMap();
headers.put("oauth_token", <OAuth token of user>);
String signature = createSignature("GET", baseUrl, headers, <OAuth secret of user>);
// Add oauth_signature to header
headers.put("oauth_signature", signature);
String body = sendGetRequest(baseUrl, headers);
return body;
}
public String sendGetRequest(String baseUrl, Map<String, String> parameters) throws AuthException, IOException {
try (CloseableHttpClient client = CloseableHttpClientFactory.getHttpClient()) {
HttpGet httpGet = new HttpGet(baseUrl);
if (parameters != null) {
httpGet.setHeader("Authorization", createHeader(parameters));
}
CloseableHttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != 200) {
LOGGER.info("GET Request Failed : " + EntityUtils.toString(response.getEntity()));
throw new Exception();
}
String responseBody = EntityUtils.toString(response.getEntity());
return responseBody;
}
}
which is the working code.
Could anyone tell me where to add parameters and what I have missed to authenticate the request?
EDIT : Added code of sendGetRequest. Making the signature and adding the header was made by following the documentations from twitter

How to do GET API Request with URL params?

Client client = ClientBuilder.newClient();
urlApi="https://localhost:123/demo/api/v1/rows/search?";
WebTarget webTarget = client.target(urlApi);
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
webTarget.queryParam(entry.getKey(), entry.getValue());
}
webTarget.queryParam("searchConditions",webTarget.queryParam("mobileNo","+9999999999"));
Invocation.Builder builder = webTarget.request();
builder.header("id", "ABC");
String asB64 = Base64.getEncoder().encodeToString("ABC:PWD".getBytes("utf-8"));
logger.debug("Calling API "+urlApi);
builder.header("Authorization", "Basic "+asB64);
builder.header("Content-type", MediaType.APPLICATION_JSON);
response = builder.get();
responseData = response.readEntity(String.class);
System.out.println(responseData);
I am trying to do GET request with searchCondition as Key and value as {mobileNo="+919999999999"}, I am unable to get this to work.
Apart from that, how can I print the Request "Headers" along with "Query params"? Thank you in advance
I think you need to encode the value inputs, something like this:
webTarget.queryParam("searchCondition", URLEncoder.encode("{mobileNo=\"+919999999999\"}", StandardCharsets.UTF_8.toString()));
UDPATE:
Example of the rest client with Spring:
#Test
public void testStack() throws Exception {
RestTemplate rest = new RestTemplate();
String fooResourceUrl="http://localhost:8080/usersParam?";
RestTemplate restTemplate = new RestTemplate();
String parameter = "{mobileNo=\"+919999999999\"}";
ResponseEntity<String> response = restTemplate.getForEntity(fooResourceUrl + "parameter=" + URLEncoder.encode(parameter, StandardCharsets.UTF_8.toString() ), String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
And this would be the rest service:
#RequestMapping(method = RequestMethod.GET, value="/usersParam")
public User getUsersInfo(#RequestParam String parameter) throws UnsupportedEncodingException {
System.out.println(URLDecoder.decode(parameter, StandardCharsets.UTF_8.toString() ));
return null;
}

Jersey converting from ClientResponse to Response

I'm currently using Jersey as a proxy REST api to call another RESTful web service. Some of the calls will be passed to and from with minimal processing in my server.
Is there a way to do this cleanly? I was thinking of using the Jersey Client to make the REST call, then converting the ClientResponse into a Response. Is this possible or is there a better way to do this?
Some example code:
#GET
#Path("/groups/{ownerID}")
#Produces("application/xml")
public String getDomainGroups(#PathParam("ownerID") String ownerID) {
WebResource r = client.resource(URL_BASE + "/" + URL_GET_GROUPS + "/" + ownerID);
String resp = r.get(String.class);
return resp;
}
This works if the response is always a success, but if there's a 404 on the other server, I'd have to check the response code. In other words, is there clean way to just return the response I got?
There is no convenience method as far as I am aware. You can do this:
public Response getDomainGroups(#PathParam("ownerID") String ownerID) {
WebResource r = client.resource(URL_BASE + "/" + URL_GET_GROUPS + "/" + ownerID);
ClientResponse resp = r.get(ClientResponse.class);
return clientResponseToResponse(resp);
}
public static Response clientResponseToResponse(ClientResponse r) {
// copy the status code
ResponseBuilder rb = Response.status(r.getStatus());
// copy all the headers
for (Entry<String, List<String>> entry : r.getHeaders().entrySet()) {
for (String value : entry.getValue()) {
rb.header(entry.getKey(), value);
}
}
// copy the entity
rb.entity(r.getEntityInputStream());
// return the response
return rb.build();
}
for me answer from Martin throw:
JsonMappingException: No serializer found for class sun.net.www.protocol.http.HttpURLConnection$HttpInputStream
Change from
rb.entity(r.getEntityInputStream());
to
rb.entity(r.getEntity(new GenericType<String>(){}));
helped.

Categories