This question is an extension to How do I set params for WS.post() in play 2.1 Java
My web service request handler is as follows
#POST
#Path("/requestPath")
public String addChallengersToLeague(
#FormParam("name") String name,
#FormParam("values") List values);
since WSRequestHolder accepts a Map<String, String> in setQueryParameter method, I am not able to send parameter list with same name.
I can send request from POSTMAN with multiple parameters having name 'values'and it works fine.
Can you suggest how to do the same using play? I am using play 2.1.3
Thanks in advance.
This can be done using play.libs.WS.WSRequest API
Following is a simple example
WSRequest request = new WSRequest("<Method>"); //Method can be GET, POST etc
request.setUrl("<service-url>");
request.setHeader("Content-Type", "application/x-www-form-urlencoded");
com.ning.http.client.FluentStringsMap map = new com.ning.http.client.FluentStringsMap();
map.add("name", "aniket");
Set<String> values= new HashSet<String>();
values.add("1");
values.add("2");
values.add("3");
values.add("4");
map.add("values", values);
request.setQueryParameters(map);
Promise<Response> response = request.execute();
You can then use response.get().getBody() to get response body.
Related
I'm falling into a problem this morning with a custom request between two application, what i need to do is to let application able to talk eachother with two Rest API cause i need to do some actions on the first application by the second. The two applications are developed with springboot.
Suppose to call this two applications admin and superadmin
superadmin send a request with a RestAPI and a customized header -> name = key value = 1234
admin recieve the request and first of all check if the header is present or not, after that the header is finded it can proceed to do all the task.
Here's the code that i've developed :
SUPERADMIN :
#PostMapping(value="/test_api_header")
public ResponseEntity<String> test_API(#RequestParam String url) {
RestTemplate template = new RestTemplate();
URI targetUrl = UriComponentsBuilder.fromUriString(url) // Build the base link
.path("/test_API") // Add path
.build() // Build the URL
.encode() // Encode any URI items that need to be encoded
.toUri(); // Convert to URI
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "application/json");
headers.add("superadminKey", "123456abc");
// build the request
ResponseEntity<String> entity = template.exchange(targetUrl, HttpMethod.GET, new HttpEntity<String>(headers), String.class);
return entity;
}
ADMIN :
#Value("123456abc")
private String saKey;
#GetMapping(value = "/superadmin/test_API")
public String test_API(HttpServletRequest request) {
if (request.getHeader("superadminKey") == saKey) {
return "Finally";
} else {
return "Nothing to do, header not present";
}
}
The SUPERADMIN is able to communicate with the RESTApi in the ADMIN application, in fact on postman i received the answer : Nothing to do, header not present, but i really cannot be able to set that customized header in the superadmin request cause i cannot found it also on postman request in the section "headers".
I've seen that i could also create a customized API Key for this special case, but really don't know how it works, if someone could help me I would be very grateful!
I am having a piece of code like below for calling one of our service.
MultiValueMap<String, String> parametersMap = new LinkedMultiValueMap<>();
parametersMap.add("query", query);
parametersMap.add("locale", "en_US");
parametersMap.add("resultsLimit", Boolean.FALSE.toString());
parametersMap.add("maxResults", maxResults);
parametersMap.add("type", "TTT");
parametersMap.add("ids", commaSeparatedValues(ids));
parametersMap.add("infoTypes", "HHH,JJJ");
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(parametersMap, getHttpHeaders());
MyEntity myEntity = restTemplate.postForEntity("http://example.com", httpEntity, MyEntity.class);
And at the server side the controller code is like
#RequestMapping("my/service")
public MyEntity suggest(#RequestParam(required = true) String query, #RequestParam(required = true) String locale,
#RequestParam(required = false) String resultsLimit, #Valid OtherOptions options)
and the OtherOption class is like
class OtherOptions {
String maxResults;
String type;
String ids;
String infoTypes;
}
Here everything is working fine, but I am confused about somethings like .
Is it a get or post request ?
How is some of the parameter maps content become request params(query params) and some others got mapped to the Object of OtherOptions ?
Which is the actual body of the request ?
Is it a get or post request ?
It is a post request. you are calling restTemplate.postForEntity. But your server side method is not restricted as you didn't specify the method attribute for RequestMapping so same method can handle any http method from the point of server.
How is some of the parameter maps content become request params(query params) and some others got mapped to the Object of OtherOptions?
None of them are query params.
See the spring docs for the meaning of #RequestParam. In your case, it all comes from body and not as query params
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#postForEntity-java.net.URI-java.lang.Object-java.lang.Class-
The body of the entity, or request itself, can be a MultiValueMap to create a multipart request.
Which is the actual body of the request?
parametersMap is the body of the http request.
Note:
Currently your call should fail because you are posting it to http://example.com at client and listening at server side on my/service
I am trying to consume the following HTTPS endpoints from Yahoo Weather Service:
Yahoo Weather Service API
I am doing some special query according to the API to get the current weather at some parametrized location.
#Service("weatherConditionService")
public class WeatherConditionServiceImpl implements WeatherConditionService {
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public WeatherCondition getCurrentWeatherConditionsFor(Location location) {
RestTemplate restTemplate = new RestTemplate();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(URL);
stringBuilder.append("?q=select%20item.condition%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22");
// TODO: Validate YQL query injection
stringBuilder.append(location.getName());
stringBuilder.append("%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys");
WeatherQuery weatherQuery = restTemplate.getForObject(stringBuilder.toString(), WeatherQuery.class);
// TODO: Test Json mapping response
Condition condition = weatherQuery.getQuery().getResults().getChannel().getItem().getCondition();
return new WeatherCondition(condition.getDate(), Integer.parseInt(condition.getTemp()), condition.getText());
}
Location is a class that provides the attribute "name" that is a String description of the location, such as "New York" or "Manila".
Condition an other classes just map the returning object.
When executing I get the following HTTP response:
org.springframework.web.client.HttpClientErrorException: 403 Forbidden
So this means I am not authorized to access the resource from what I understand.
The URL works great if I just copy & paste it in a web browser:
Yahoo Weather Query
I think that mapping is not a problem since I am not getting "400" (Bad Request) but "403" (Forbidden)
There must be some error on the way I use the RestTemplate object. I am researching but I can't find an answer.
The docs say you need an api key. But when I make a call like this:
fetch('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22nome%2C%20ak%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys')
.then(resp=> resp.json())
.then((res)=>console.log(res.query.results))
https://repl.it/NeoM
It works fine without one. Perhaps you've been blackisted for hitting the api too often.
Your code seems fine.
I finally found the answer. It finally WAS a Bad Request because I needed to pass the parameters differently (not as part of the URL).
I found the answer here. Here goes the code for my particular Yahoo Weather API call return a String (I still will have to do some work to use the mapping).
private static final String URL = "http://query.yahooapis.com/v1/public/yql";
public String callYahooWeatherApi() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(URL)
.queryParam("q", "select wind from weather.forecast where woeid=2460286")
.queryParam("format", "json");
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.build().encode().toUri(),
HttpMethod.GET,
entity,
String.class);
return response.getBody();
}
I'm trying to send a post request to github to create a repository. I've got oauth 2.0 working and the request is correctly signed but github is just returning "Problems parsing JSON"
I'm using Scribe for the oauth side of things and as far as I can tell I've added JSON to the URL but I'm not 100% certain I'm doing it correctly, or am I just missing headers or something?
#POST
#Path("create_repo/{userid}")
#Produces(MediaType.APPLICATION_JSON)
public Response createRepo(#PathParam("userid") String userid) {
OAuthService service = createService().build();
User user = collection.findOneById(userid);
final OAuthRequest request = new OAuthRequest(Verb.POST, "https://api.github.com/user/repos", service);
Token token = new Token(user.getGithubToken(), "SECRET");
service.signRequest(token, request);
request.addHeader("Content-type", "application/vnd.github.v3+json");
request.addHeader("X-OAuth-Scopes", "repo");
request.addQuerystringParameter("name", "Test_v1");
LOGGER.info("Built request: " + request.getCompleteUrl());
final com.github.scribejava.core.model.Response response = request.send();
return Response.ok(response.getBody()).build();
}
The built URL looks like: https://api.github.com/user/repos?access_token=XXX_SECRET_XXX&name=Test_v1
I've also tried swapping the access_token after the params but same result.
Appreciate the any help.
Well I solved this by creating a object, serializing it, and adding it as a payload.
#POST
#Path("create_repo/{userId}/{projectId}")
#Produces(MediaType.APPLICATION_JSON)
public Response createRepo(#PathParam("userId") String userId, #PathParam("projectId") String projectId) {
// Setup collections
User user = userCollection.findOneById(userId);
ProjectDescription projectDescription = projectCollection.findOneById(projectId);
// Build repository object from project description
GithubRepository repository = new GithubRepository();
repository.setName(projectDescription.getTitle());
repository.setDescription(projectDescription.getDescription());
// Serialize object
ObjectMapper mapper = new ObjectMapper();
String jsonInString = null;
try {
jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(repository);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// Build request
OAuthService service = createService().build();
final OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URL + "/user/repos", service);
request.addHeader("content-type", "application/json");
request.addPayload(jsonInString);
// Sign and send request
Token token = new Token(user.getGithubToken(), "secret");
service.signRequest(token, request);
request.send();
return Response.status(201).build();
}
However, I'd still like to know where I went wrong with my first attempt.
Query string parameters are ignored in POST requests. That's why it worked when passing them in the request body.
From GitHub's API Overview docs:
Parameters
Many API methods take optional parameters. For GET requests, any parameters not specified as a segment in the path can be passed as an HTTP query string parameter:
curl -i "https://api.github.com/repos/vmg/redcarpet/issues?state=closed"
In this example, the ‘vmg’ and ‘redcarpet’ values are provided for the :owner and :repo parameters in the path while :state is passed in the query string.
For POST, PATCH, PUT, and DELETE requests, parameters not included in the URL should be encoded as JSON with a Content-Type of ‘application/json’:
$ curl -i -u username -d '{"scopes":["public_repo"]}' https://api.github.com/authorizations
I've already looked at How to add Headers on RESTful call using Jersey Client API, however this is for Jersey 1.x.
How do I set a header value (such as an authorization token) in Jersey 2.21?
Here is the code I'm using:
public static String POST(final String url, final HashMap<String, String> params)
{
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target(url);
String data = new Gson().toJson(params);
Entity json = Entity.entity(data, MediaType.APPLICATION_JSON_TYPE);
Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON_TYPE);
return builder.post(json, String.class);
}
In Jersey 2.0+, you can register a custom implementation of ClientRequestFilter that can manipulate the headers in the request that the Client API will send out.
You can manipulate the headers via the ClientRequestContext parameter that is passed into the filter method. The getHeaders() method returns the MultivaluedMap on which you can put your header(s).
You can register your custom ClientRequestFilter with your ClientConfig before you call newClient.
config.register(MyAuthTokenClientRequestFilter.class);
If you want to add only few headers in Jersey 2.x client, you can simply add it when request is sending as follows.
webTarget.request().header("authorization":"bearer jgdsady6323u326432").post(..)...
To add to what Pradeep said, there's also headers(MultivaluedMap < String, Objects> under WebTarget.request() if you have a gaggle of headers:
MultivaluedMap head = new MultivaluedHashMap();
head.add("something-custom", new Integer(10));
head.add("Content-Type", "application/json;charset=UTF-8");
builder.headers ( head ); // builder from Joshua's original example