I am sending some get query to an API that returns a 400 Bad request when I send empty query parameters.
However, I do not know in advance which optional query parameters will be empty.
Apart from testing each optional parameter for emptyness, is there a simpler approach to dynamically remove empty parameters ?
Currently I am doing:
private URI getApiUrl(QueryParametersObject qpo) {
String url = configBundle.getPropertyStr("some.api.url");
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(url)
.queryParam("mandatoryParam1", qpo.getMandatoryParam1()) // MANDATORY
.queryParam("mandatoryParam2", qpo.getMandatoryParam2()); // MANDATORY
if (!qpo.getOptionalParam1().isEmpty())
builder.queryParam("optionalParam1", qpo.getOptionalParam1());
if (!qpo.getOptionalParam2().isEmpty())
builder.queryParam("optionalParam2", qpo.getOptionalParam2());
if (!qpo.getOptionalParam3().isEmpty())
builder.queryParam("optionalParam3", qpo.getOptionalParam3());
return builder.build().encode().toUri();
}
It can become quite cumbersome when the number of optional query parameters grows.
A way to do this is to use a newer version of spring-web, like 5.3.4, in which the class UriComponentsBuilder gets a new method UriBuilder queryParamIfPresent(String name, Optional<?> value) that you can use like this:
private URI getApiUrl(QueryParametersObject qpo) {
String url = configBundle.getPropertyStr("some.api.url");
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(url)
.queryParam("mandatoryParam1", qpo.getMandatoryParam1()) // MANDATORY
.queryParam("mandatoryParam2", qpo.getMandatoryParam2()) // MANDATORY
.queryParamIfPresent("optionalParam1", Optional.of(qpo.getOptionalParam1()))
.queryParamIfPresent("optionalParam2", Optional.of(qpo.getOptionalParam2()))
.queryParamIfPresent("optionalParam3", Optional.of(qpo.getOptionalParam3()));
return builder.build().encode().toUri();
}
Further enhancing the accepted answer by #l0r3nz4cc10
Optional.ofNullable would be a better approach !!!
private URI getApiUrl(QueryParametersObject qpo) {
String url = configBundle.getPropertyStr("some.api.url");
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(url)
.queryParam("mandatoryParam1", qpo.getMandatoryParam1()) // MANDATORY
.queryParam("mandatoryParam2", qpo.getMandatoryParam2()) // MANDATORY
.queryParamIfPresent("optionalParam1", Optional.ofNullable(qpo.getOptionalParam1()))
.queryParamIfPresent("optionalParam2", Optional.ofNullable(qpo.getOptionalParam2()))
.queryParamIfPresent("optionalParam3", Optional.ofNullable(qpo.getOptionalParam3()));
return builder.build().encode().toUri();
Related
Im working in a Spring Reactive application. I know how get a PathVariable in a interceptor with HttpServletRequest, some like that:
request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
But we had to make some changes and now we have a WebFilter implementation, so we don't use HttpServletRequest, instead we use ServerWebExchange
How can I get a Pathvariable from ServerWebExchange? Its possible?
I think there is no straightforward solution to that.
What you can do is the following :
ServerWebExchange.getRequest() will return ServerHttpRequest object, so you can extract URI from that object like this:
URI uri = serverHttpRequest.getURI()
Then, using UriTemplate you should be able to extract path variable values.
Here is example:
URI uri = new URI("abc.api.com/learn/sections/asdf-987/assignments/dsfwq98r7sdfg"); //suppose that your URI object is something like this
String path = uri.getPath(); //get the path
UriTemplate uriTemplate = new UriTemplate("/learn/sections/{sectionId}/assignments/{assigmentId}"); //create template
Map<String, String> parameters = new HashMap<>();
parameters = uriTemplate.match(path); //extract values form template
System.out.println(parameters);
This will produce following output:
{sectionId=asdf-987, assigmentId=dsfwq98r7sdfg}
I am trying to automate twitter API. when tried to print "js.get("text") using
System.out.println(js.get("text")); I am getting error as
"The method println(boolean) is ambiguous for the type PrintStream"
I downloaded jars and passed in Build path as well "scribejava-apis-2.5.3" and "scribejava-core-4.2.0"
Below code is not allowing me use println for ------>js.get("text")
public class Basicfunc {
String Consumerkeys= "**************";
String Consumersecretkeys="*******************";
String Token="*******************";
String Tokensecret="***************************";
#Test
public void getLatestTweet(){
RestAssured.baseURI = "https://api.twitter.com/1.1/statuses";
Response res = given().auth().oauth(Consumerkeys, Consumersecretkeys, Token, Tokensecret).
queryParam("count","1").
when().get("/home_timeline.json").then().extract().response();
String response = res.asString();
System.out.println(response);
JsonPath js = new JsonPath(response);
System.out.println(js.get("text"));
}
}
Use System.out.println(js.getString("text")); instead of System.out.println(js.get("text"));, because get returns any primitive value.
I think your problem is that your twitter response is actually a list.
Try to use System.out.println(js.getList()[0].get("text")); and be aware that you are only using the first [0] entry and ignoring the rest.
I've been to asked to refactor some code that makes a request to a web API that I have no idea what happens when it receives the request. I just have to clean up the code that makes the request. I now have this:
FormBody formBody = new FormBody.Builder()
.add("task", task.get("task"))
.add("status", task.get("status"))
.add("spent_time", task.get("spentTime"))
.add("impediments", task.get("impediments"))
.add("reoccurring", String.valueOf(task.get("reoccurring")))
.build();
return new OkHttpClient().newCall(
new Request.Builder()
.url(buildUrl("/activities/" + task.get("id")))
.method("POST", formBody)
.header("Accept", "application/json")
.build()
Originally, this function was split into three more functions.
One url call for having the forms task and status
another url call for having spent_time and impediments
and then another url call for only having reoccurring
But since they all belong to the same url, I've decided to combine them into one function because I had an idea. Though it seems my idea kinda falls short. How do I make it so that if (e.g.) task,status, and reoccurring are null, then it would only create a form that only has spent_time and impediments?
It looks like your task variable is a map of some sorts that has String keys and Object values. Here is your solution:
Builder builder = new FormBody.Builder();
String[] names = { "task", "status", "reoccurring", "spent_time", "impediments" };
for (String name : names) {
Object value = task.get(name);
if (value != null)
builder.add(name, value instanceof String ? (String) value : String.valueOf(value));
}
FormBody formBody = builder.build();
This iterates through all your form keys and checks whether the values are null or not before adding them to your form builder.
If you want you can change this to use Streams, which might be more efficient and shorter.
lets assume I have some method like that:
void getMagicUrl(Sting urlBase, String addedPath) {
urlBase = urlBase.endsWith("/") ? urlBase : urlBase + "/"; // *1*
urlBase = urlBase + "constantPath/"; // *2*
urlBase = urlBase + addedPath; // *3*
return new URL(urlBase); // *4*
}
Is there with Java (8) no better way to concatenate the URL together and not to check manual for the ending / as path separator to not forget it? (Like in line 1)
and have it in the path names always? (like in line 2)
or does the JDK have something in there?
One of the most efficient ways to build url is to use UriComponentsBuilder
Something like this
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http").host("www.blahblah.com).path("/somepath").build().encode();
uriComponents.toUriString());
This avoids all the manual concatenation using +
There are many methods to add path variables and query params and much more. Here is a nice article on it.
There are more alternatives to build a url in both type-safety and fluent way. They involve adding new dependencies but some of them are very common and useful.
Apache commons (JavaDoc)
URI uri = new URIBuilder()
.setHost("your_host")
.setPath("/you_path")
.setParameter("param1", "value1")
.build();
uri.toString();
JAX-RS (JavaDoc)
URI uri= UriBuilder
.fromPath("your_domain")
.scheme("http")
.path("your_path/")
.queryParam("param1", "value1")
.build();
URI uri = builder.build();
OkHttp (JavaDoc) (GitHub)
URL url = new HttpUrl.Builder()
.scheme("http")
.host("your_domain")
.addPathSegments("your_path")
.addQueryParameter("param1", "value1")
.build()
.url();
If you want to use Java 8 without additional dependencies, you could use URI class' constructor directly but it's not so friendly as the previous alternatives.
URI class (JavaDoc)
URI uri = new URI("your_host", "your_path", "param1=value1");
I tried to get product from API with some parameters. I used WooCommerce API Java Wrapper. REST API with OAuth 1.0. Simple getAll method return list of one page (10 products). To get all i must set how much products must be in one page and use offset. To get third page must send this parameters: "per_page=10&offset=20". I test with query in get&post programm - all work. In Java, when i added parameters - i got error (401)- "Invalid signature - the provided signature did not match".
I changed WooCommerceAPI class:
private static final String API_URL_FORMAT = "%s/wp-json/wc/v2/%s";
private static final String API_URL_ONE_ENTITY_FORMAT = "%s/wp-json/wc/v2/%s/%d";
private HttpClient client;
private OAuthConfig config;
public List getAll(String endpointBase) {
String url = String.format(API_URL_FORMAT, config.getUrl(), endpointBase) + "?per_page=10&offset=20";
String signature = OAuthSignature.getAsQueryString(config, url, HttpMethod.GET);
String securedUrl = String.format("%s&%s", url, signature);
System.out.println("url="+url);
System.out.println("securedUrl="+securedUrl);
return client.getAll(securedUrl);
}
But I have got the same error.
I've just released a new version of wc-api-java library (version 1.2) and now you can use the method getAll with params argument where you can put additional request parameters. For example:
// Get all with request parameters
Map<String, String> params = new HashMap<>();
params.put("per_page","100");
params.put("offset","0");
List products = wooCommerce.getAll(EndpointBaseType.PRODUCTS.getValue(), params);
System.out.println(products.size());
As you noticed, you changed URL_SECURED_FORMAT from "%s?%s" to "%s&%s", as soon as you added query params. But problem is that signature is generated based on all query params, not only oauth_*, and your params offset and per_page are ignored while generating signature (as soon as lib author did not expect additional params).
Think that you need to modify this lib to support signature based on all params.