Pass Multiple parameters in a REST call - rest

My server code is as:
#POST
#Path("/getMapping")
public ListResponse getMapping(Long id, String name, String clientName, String instanceName) {
ListResponse response = null;
try {
response = new ListResponse();
List<Mappings> mappings = service.getMapping(id, name, clientName, instanceName);
response.setStatusCode(SUCCESS);
response.setMappings(mappings);
} catch (Exception e) {
setResponseErrors(response, e);
}
return response;
}
I am using Jersey REST client, but I dont think there is an option to have multiple params passed in the post method like:
ClientResponse clientResponse = webResource.type(XML_TYPE).post(ClientResponse.class, id, name, clientName, instanceName);
Is there a way to accomplish this?
I could use MultiValuedMap or #QueryParams in this case, but there are other cases where multiple params are more complex Objects. Also, wrapping all in a "paramContainer" will be an inefficient solution since there are so many such methods with multiple params with different combinations.
(As an aside, why would REST not support multiple params?)
Any help greatly appreciated.

here is how I'll do it
SERVER CODE
1.1 should have to use #FormParam in order to declare parameters in #FormDataParam
1.2 a POST is better if encrypted for that use #Consumes(MediaType.MULTIPART_FORM_DATA)
you will have a server code like this :
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("/getMapping")
public ListResponse getMapping(#FormParam("id")Long id, #FormParam("name") String name, #FormParam("clientName") String clientName, #FormParam("instanceName") String instanceName) {
ListResponse response = null;
try {
response = new ListResponse();
List<Mappings> mappings = service.getMapping(id, name, clientName, instanceName);
response.setStatusCode(SUCCESS);
response.setMappings(mappings);
} catch (Exception e) {
setResponseErrors(response, e);
}
return response;
}
CLIENT CODE
Form form = new Form();
form.add("id", "1");
form.add("name", "je#rizze.com");
form.add("clientName","firefox");
form.add("instanceName","node45343.rizze.com");
ClientResponse response = webResource
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
.post(ClientResponse.class, form);
enjoy :)

An addition to jeorfevre's answer above:
In case you're using Jersey 1.x, this is how it works:
Client: (pure Java):
public Response testPost(String param1, String param2) {
// Build the request string in this format:
// String request = "param1=1&param2=2";
String request = "param1=" + param1+ "&param2=" + param2;
WebClient client = WebClient.create(...);
return client.path(CONTROLLER_BASE_URI + "/test")
.post(request);
}
Server:
#Path("/test")
#POST
#Produces(MediaType.APPLICATION_JSON)
public void test(#FormParam("param1") String param1, #FormParam("param2") String param2) {
...
}

Related

400 error when making post request to Spring server

Hi I'm very new to Swift and trying to make a simple application.
I'm using 'alamofire 5 beta 6' to make a request.
Here is some code below
-code for making post request
var json:JSON = JSON(["id":id.text, "password":enteredPassword])
var parameters: Parameters = ["id":id.text, "password":enteredPassword]
let headers:HTTPHeaders = [ "Content-Type":"application/json"]
AF.request("http://127.0.0.1:8080/user", method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers).responseJSON{
response in
print("response : \(response)")
}
-code for Spring Framework
#RequestMapping(value="user", method=RequestMethod.POST)
public JSONObject addUser(
#RequestBody Memberinfo member,
HttpServletRequest request) {
JSONObject result = new JSONObject();
return result;
}
-Memberinfo.java that is used in controller to retrieve #RequestBody
public class Memberinfo {
String id;
String password;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
In Swift code, I set parameter id and password to retrieve it back in Spring framework.
However, right after I make a request, Alamofire responses with message
response : success({
error = "Bad Request";
message = "JSON parse error: Unrecognized token 'id': was expecting ('true', 'false' or 'null'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'id': was expecting ('true', 'false' or 'null')\n at [Source: (PushbackInputStream); line: 1, column: 4]";
path = "/user";
status = 400;
timestamp = "2019-06-09T05:46:07.417+0000";
})
I think the parameter should be sent as following
var parameters: Parameters = {"id":id.text, "password":enteredPassword}
Another important thing that you don't need to have a JSONObject as a response type for your endpoint you can just annotate your endpoint with #ReponseBody to have a json response
#RequestMapping(value="user", method=RequestMethod.POST)
#ResponseBody
public ResponseEntity<JSONObject> addUser(
#RequestBody Memberinfo member,
HttpServletRequest request) {
JSONObject result = new JSONObject();
return result;
}
Always use something like Postman or RestClient to test your endpoint before calling this endpoint from an external source

REST Response is {}

I am new to REST. I have written a small REST resource and Whenever I try to invoke the REST service from POSTMAN, i get a empty response {} and status code 200
The Request :
http://localhost:8080/demo/managers
#GET
#Path("managers")
#Produces({"application/json"})
public Response getManagers() throws GeneralException, JSONException
{
JSONArray valueString = COMING_FROM_OTHER_METHOD();
System.out.println("==== "+valueString.toString());
return Response.ok(valueString,MediaType.APPLICATION_JSON).build();
}
The correct value I can see in System.out.println():
[{"display":"john","id":"003"},{"display":"hansi","id":"004"},{"display":"samy gayle","id":"005"}]
I want to a JSONArray Response but everytime I get an empty response
{}
But when modify the code like below it gives correct response
#GET
#Path("managers")
#Produces({"application/json"})
public String getManagers() throws GeneralException, JSONException
{
JSONArray valueString = COMING_FROM_OTHER_METHOD();
System.out.println("==== "+valueString.toString());
return valueString.toString();
}
Kindly Help. why am I getting {} when trying to return a Response object J
I would use domain objects rather than String instances:
class Manager {
private String id;
private String display;
... setters/getters ...
}
public ResponseEntity<ArrayList<Manager>> getManagers() throws GeneralException {
ArrayList<Manager> managers = COMING_FROM_OTHER_METHOD();
return new ResponseEntity<>(managers, HttpStatus.OK);
}

Retrofit 2.0-beta-2 is adding literal quotes to MultiPart values

Went to upgrade to Retrofit 2.0 and running into this weird problem.
I have a method to log a user in
public interface ApiInterface {
#Multipart
#POST("user/login/")
Call<SessionToken> userLogin(#Part("username") String username, #Part("password") String password);
}
When I look at the key value POST params on the server side they print like this
username : "brian"
password : "password"
The same method using retrofit 1.9 the K:V pairs look like
username : brian
password : password
It's adding literal quotes to the POST variables
If I use any other rest client the variables print like the second way without the quotes.
Here is how I build the Retrofit instance with an interceptor
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Customize the request
Request request = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization", myPrefs.accessToken().getOr(""))
.method(original.method(), original.body())
.build();
Response response = chain.proceed(request);
// Customize or return the response
return response;
}
});
Ok2Curl.set(client);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(apiEndpoint)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
I imagine i'm doing something wrong with the converter but not sure what.
Has anyone else ran into this problem yet? I know its in beta but it's pretty widly used.
This is because it's running through the JSON converter.
Solution1:
use RequestBody instead of String
public interface ApiInterface {
#Multipart
#POST("user/login/")
Call<SessionToken> userLogin(#Part("username") RequestBody username, #Part("password") RequestBody password);
}
Build RequestBody:
RequestBody usernameBody = RequestBody.create(MediaType.parse("text/plain"), usernameStr);
RequestBody passwordBody = RequestBody.create(MediaType.parse("text/plain"), passwordStr);
Launch network operation:
retrofit.create(ApiInterface.class).userLogin(usernameBody , passwordBody).enqueue()....
Solution2: Create a custom ConverterFactory to dispose String part value.
For: Retrofit2 final release not beta. (com.squareup.retrofit2:retrofit:2.0.0)
Create your StringConverterFactory:
public class StringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
public static StringConverterFactory create() {
return new StringConverterFactory();
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<ResponseBody, String>() {
#Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
};
}
return null;
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if(String.class.equals(type)) {
return new Converter<String, RequestBody>() {
#Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
Add to your retrofit instance:
retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.client(client)
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
Attention: StringConverterFactory should add before GsonConverterFactory!
then you can use String as part value directly.
You can find more information about this issue in https://github.com/square/retrofit/issues/1210
I have the same problem, and how it solved:
1) Add to build.gradle:
compile 'com.squareup.retrofit2:converter-scalars:2.1.0' // Remember to add the same version
2) Add one line here:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create()) // this line
.addConverterFactory(GsonConverterFactory.create(gson))
.client(getUnsafeOkHttpClient())
.build();
What about to do in that way?
RequestBody caption = RequestBody.create(MediaType.parse("text/plain"), new String("caption"));
Here is how to resolve it,
Firstly:
return new Retrofit.Builder()
.baseUrl(Env.GetApiBaseUrl())
.addConverterFactory(new GsonStringConverterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(getHttpClient())
.build();
Create a CustomConverter like this one, this is needed by Retrofit 2, unless some fix the "feature" added in v2.
public class GsonStringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
#Override
public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
if (String.class.equals(type))// || (type instanceof Class && ((Class<?>) type).isEnum()))
{
return new Converter<String, RequestBody>() {
#Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
I've found another one solution except those. Worked with Retrofit 2.1.0. (Rx adapter is optional here)
My retrofit interface looks like this:
#POST("/children/add")
Observable<Child> addChild(#Body RequestBody requestBody);
And in ApiManager I use it like this:
#Override
public Observable<Child> addChild(String firstName, String lastName, Long birthDate, #Nullable File passportPicture) {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("first_name", firstName)
.addFormDataPart("last_name", lastName)
.addFormDataPart("birth_date", birthDate + "");
//some nullable optional parameter
if (passportPicture != null) {
builder.addFormDataPart("certificate", passportPicture.getName(), RequestBody.create(MediaType.parse("image/*"), passportPicture));
}
return api.addChild(builder.build());
}
It is similar to Solution1 from Loyea but I think that it's little a bit more elegant.
If your UI is showing your responses with quotes, you can use getAsString instead of toString
I don't know if it is too late, but we can also send requests with RequestBody.
Example:
public interface ApiInterface {
#Multipart
#POST("user/login/")
Call<SessionToken> userLogin(#Part("username") String username, #Part("password") String password);
}
We can convert as below:
public interface ApiInterface {
#Multipart
#POST("user/login/")
Call<SessionToken> userLogin(#Part("username") RequestBody username, #Part("password") String password);
}

Return more that one entity in RESTful response?

I'm developing a RESTful websevices project, my question is simple,
is there a way to return both 'File' and 'JSON' entities in the same response?
e.g.: suppose I have this method:
#GET
#Path("downloadFile")
#Consumes(MediaType.TEXT_PLAIN)
public Response downloadLogStream( ..... ) {
.....
Response.ok(resultFile);
}
but I need to return another entity beside the file itself without adding additional Headers.
is that possible?
You can send only response but that can be a complex object. Wrap result/json and status in response.
return Response.status(200).entity(result).build();
More here
#GET
#Path("downloadFile")
#Consumes(MediaType.TEXT_PLAIN)
public Response downloadLogStream( ..... ) {
// Assuming result is json string
String result = " JSON is "+jsonObj;
return Response.status(200).entity(result).build();
}
}
File download
private static final String FILE_PATH = "pathTo:\\filename. Zip";
#GET #Path("/get")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile()
{
File file = new File(FILE_PATH);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename=newfile.zip");
return response.build(); }

Jersey client. MultivaluedMap goes empty

My RESTful client has this method:
public void testGetCateogrywiseData() {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
client.addFilter(new LoggingFilter(System.out));
WebResource service = client
.resource("http://localhost:8080/MyApp/rest/publicdata");
#SuppressWarnings("rawtypes")
MultivaluedMap queryParams = new MultivaluedMapImpl();
queryParams.add("latitude", "18.522387");
queryParams.add("longitude", "73.878437");
queryParams.add("categoryID", "2");
service.queryParams(queryParams);
ClientResponse response = service.get(ClientResponse.class);
System.out.println(response.getStatus());
System.out.println("Form response " + response.getEntity(String.class));
}
On the server side the method looks like this:
#Path("publicdata")
#GET
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String getPublicData() throws JSONException {
MultivaluedMap<String, String> valueMap = uriInfo.getQueryParameters();
Long latString = Long.parseLong(valueMap.getFirst("latitude"));
Long lonString = Long.parseLong(valueMap.getFirst("longitude"));
Long categoryId = Long.parseLong(valueMap.getFirst("categoryID"));
// Do necessary stuff and return json string
return null;
}
My problem is the valueMap at the server end is always empty. It never gets the three parameters that I have sent from the client code. What am I missing?
The problem happens on this line:
service.queryParams(queryParams);
It successfully adds the query params, but it does not change the original service, it returns a new one to you. To make it work you need to change to this:
service = service.queryParams(queryParams);