Spring Rest : Handling POST requests, at server end - android

I am asking this question based on the answers in this link
POST request via RestTemplate in JSON
I actually wanted to send JSON from client and receive the same at REST server. Since the client part is done in the link I mentioned above. For the same how would I handle that request at server end.
CLIENT:
// create request body
JSONObject request = new JSONObject();
request.put("username", name);
request.put("password", password);
// set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
// send request and parse result
ResponseEntity<String> loginResponse = restTemplate
.exchange(urlString, HttpMethod.POST, entity, String.class);
if (loginResponse.getStatusCode() == HttpStatus.OK) {
JSONObject userJson = new JSONObject(loginResponse.getBody());
} else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) {
// nono... bad credentials
}
SERVER:
#RequestMapping(method=RequestMethod.POST, value = "/login")
public ResponseEntity<String> login(#RequestBody HttpEntity<String> entity) {
JSONObject jsonObject = new JSONObject(entity.getBody());
String username = jsonObject.getString("username");
return new ResponseEntity<>(username, HttpStatus.OK);
}
This gives me 400 bad request error at client side. Hoping for some clues about how to handle this at server side.

HTTPEntity should not be used in your server method. Instead use the argument which is being passed to HTTPEntity from your client. In your case it has to String since you are passing string from client. Below code should work for you.
#RequestMapping(method=RequestMethod.POST, value = "/login")
public ResponseEntity<String> login(#RequestBody String jsonStr) {
System.out.println("jsonStr " + jsonStr);
JSONObject jsonObject = new JSONObject(jsonStr);
String username = jsonObject.getString("username");
return new ResponseEntity<String>(username, HttpStatus.OK);
}
My advice is to create bean class and use it in server and client instead of converting it to String. It will improve readability of the code.

When using the Spring RestTemplate, I usually prefer to exchange objects directly. For example:
Step 1: Declare and define a data holder class
class User {
private String username;
private String password;
... accessor methods, constructors, etc. ...
}
Step 2: Send objects of this class to the server using RestTemplate
... You have a RestTemplate instance to send data to the server ...
// You have an object to send to the server, such as:
User user = new User("user", "secret");
// Set HTTP headers for an error-free exchange with the server.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Generate an HTTP request payload.
HttpEntity<User> request = new HttpEntity<User>(user, headers);
// Send the payload to the server.
restTemplate.exchange("[url]", [HttpMethod], request, User.class);
Step 3: Configure a ContentNegotiatingViewResolver on the server
Declare a bean of the type ContentNegotiatingViewResolver in the Spring XML or Java configuration. This will help the server automatically bind HTTP requests with bean objects.
Step 4: Receive the request on the server
#RestController
#RequestMapping("/user")
class UserAPI {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public User create(User user) {
// Process the user.
// Possibly return the same user, although anything can be returned.
return user;
}
}
The ContentNegotiatingViewResolver ensures that the incoming request gets translated into a User instance without any other intervention.
Step 5: Receive the response on the client
// Receive the response.
HttpEntity<User> response = restTemplate.exchange("[url]", [HttpMethod], request, User.class);
// Unwrap the object from the response.
user = response.getBody();
You will notice that the client and the server both use the same bean class (User). This keeps both in sync as any breaking change in the bean structure would immediately cause a compilation failure for one or both, necessitating a fix before the code is deployed.

Related

Android, OkHttp, AWS4Signer, aws-api-gateway - "Missing Authentication Token"

i am trying to sign a http request to aws api gateway in android using okhttp. i have more or less used the code in this stackoverflow question stackoverflow question
i use CognitoCachingCredentialsProvider() to get a credentialsProvider object. i then use getCredentials() to get the credentials. i then use the following: credentials.getAWSAccessKeyId(), credentials.getAWSSecretKey() and credentials.getSessionToken() to get the necessary keys and token. i use them in postman and am able to successfully execute the api gateway.
the request fails in android using okhttp, returning a code 403 with the message "Missing Authentication Token".
this is how i prepare the request: i build a DefaultRequest object, setting the endpoint and httpmethod. i then use AWS4Signer to sign the request, passing the credentials object as the signer.sign(defaultRequest, credentials) parameter.
i get a map of headers by calling getHeaders() on the defaultRequest. i create two lists, one called key for the key and one called value for the value. i then loop through the map, loading the keys and corresponding values into the two lists.
i then build my okhttp request as follows:
Request request = new Request.Builder()
.url(my ApiEndPoint)
.addHeader(key.get(0), value.get(0))
.addHeader(key.get(1), value.get(1))
.addHeader(key.get(2), value.get(2))
.addHeader(key.get(3), value.get(3))
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.post(body)
.build();
i notice the following:
in the headers map, key x-amz-security-token has a value ....ending in hKADF87VZ44w9IvZ1gU=
printing out the okhttp request, the key x-amz-security-token has a value .... ending in hKADF87VZ44w9IvZ1gU\u003d
the = is replaced by \u003d, could this be the problem? if so, how to prevent this?
otherwise, any help in solving this problem will be greatly appreciated.
thanks
managed to solve the problem. seems that assigning the headers to the OkHttp request was the problem. so here's my code:
i first get AWSSessionCredentials credentials. then:
AmazonWebServiceRequest amazonWebServiceRequest = new AmazonWebServiceRequest() {
};
String API_GATEWAY_SERVICE_NAME = "execute-api";
com.amazonaws.Request requestAws = new DefaultRequest(amazonWebServiceRequest, API_GATEWAY_SERVICE_NAME);
you can use either the service endpoint:
URI uri = URI.create("https://apigateway.eu-west-1.amazonaws.com");
or your api url (the invoke url for api as per Api Gateway console Stages option (The deployed api)):
String invokeUrl = "https://xxxx.execute-api.eu-west-1.amazonaws.com/yyy/zzzzz";
// using the invoke url
URI uri = URI.create(invokeUrl);
requestAws.setEndpoint(uri);
requestAws.setResourcePath(invokeUrl);
requestAws.setHttpMethod(HttpMethodName.POST);
now sign the request
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(API_GATEWAY_SERVICE_NAME);
signer.setRegionName(Region.getRegion(Regions.EU_WEST_1).getName());
signer.sign(requestAws, credentials);
get the headers
// get map of headers
Map<String, String> headers = requestAws.getHeaders();
// create objects for the headers to add manually in OkHttp request builder
String x_date = null;
String x_token = null;
String authorization = null;
//get and assign values
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equals("x-amz-security-token")) {
x_token = entry.getValue();
}
if (entry.getKey().equals("X-Amz-Date")) {
x_date = entry.getValue();
}
if (entry.getKey().equals("Authorization")) {
authorization = entry.getValue();
}
}
build the OkHttp request:
Request request = new Request.Builder()
.url(invokeUrl)
.addHeader("Content-Type", "application/json")
.addHeader("X-Amz-Date", x_date)
.addHeader("x-amz-security-token", x_token)
.addHeader("Authorization", authorization)
.post(body)
.build();
now make your OkHttp call.
hope this is helpful to someone.

Okhttp Put method returning 'Method Not Allowed'

I am attempting to call a put method on my server using OkHttp from an Android application.
This is the api method signature:
public void Put(int userId, string regId)
{
}
This is the Android code to call the above method:
private boolean SendGCMRegIdToServer(String registrationId, Integer userId) throws IOException {
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(serverApiHost)
.addPathSegment("AppDashboard")
.addPathSegment("api")
.addPathSegment("GCM/")
.build();
MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
String json = "{'userId':" + userId + ","
+ "'regId':'" + registrationId + "'"
+ "}";
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.put(requestBody)
.build();
//this should post the data to my server
Response response = client.newCall(request).execute();
if(response.code() == 400)
return false;
return true;
}
Now the problem is I am getting the error code 405 in the response saying Method not allowed, but I cannot see where the problem is because I can successfully call the method using Postman on the server itself as below:
http://localhost/AppDashboard/api/GCM?userId=5&regId=123
I'm thinking it may have something to do with an integer or string being passed incorrectly in the JSON string, but cannot see why this isn't working.
i had the same problem and server was returning 405 . after some search i realized that is a configuration problem on IIS that does not let put requests. so there is no problem in android code and you should config your server to let this kind of requests.
see this , this and this
Ok thanks for replies guys but seems I was getting a little confused between the two methods I was using to pass the params to my API.
Here's what I did:
changed the signature of the method to post with a param [FromBody] as a Model (only supports one paramater)...
public void Post([FromBody]UserGcmRegIdModel model)
{
}
I was then able to change my method call to the following using a nicer JSONBuilder and using .post in the request builder rather than .put
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("UserId", userId);
jsonObject.put("RegId", registrationId);
} catch (JSONException e) {
e.printStackTrace();
}
String json = jsonObject.toString();
RequestBody requestBody = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
I still don't know if there is a problem with put() methods on IIS but using a post in my case was absolutely fine so I'm going with that...
I see two different approaches in your REST api calls. In the one of OkHttp you send a PUT method with a JSON object serialized, and in POSTMAN you send a PUT (although I guess you do a GET) request with the parameters within the URL, I mean not in JSON body structure.
Anyway, HTTP 405 is telling you that your backend does not support the PUT method, and probably it's expecting a POST method with the "X-HTTP-Method-Override:PUT" HTTP header since POST is more standard method in REST than PUT.
What would I do is check your POSTMAN request carefully and adjust the one of Android to be the same method, parameters and headers, not more.
Answer Update (as question has been updated)
Of course there is a problem with that verb, as I said above IIS handles only the standard methods and PUT is not one of those. You have three choices:
Change your PUT to POST.
Use POST with X-HTTP-Method-Override to PUT. (reference)
Modify IIS config to support non standard REST methods. I
personally wouldn't suggest the 3rd one, since it's attached to the
backend config (e.g. imagine you change IIS to NancyFX).

Empty request body using SpringAndroidSpiceRequest

I'm writing an Android app, which is a client to my web application. I'm trying to use RoboSpice to perform network requests.
First of all I decided to test an API call to obtain an OAuth2 token. The following curl command can be called to obtain it from command line:
curl -X POST -d "grant_type=password&username=user&password=pass" http://testid:testsecret#localhost:8000/oauth2/token/
user and pass are the credentials for a registered user and testid and testsecret are the id and secret of a registered app in my web application. This call returns a JSON object with a token and other parameters.
I'm trying to do the same request using RoboSpice. Here's the code I wrote for the request:
public class OAuth2Request extends SpringAndroidSpiceRequest<String> {
private final String user;
private final String pass;
public OAuth2Request(String user, String pass) {
super(String.class);
setRestTemplate(new RestTemplate());
getRestTemplate().getMessageConverters().add(new StringHttpMessageConverter());
this.user = user;
this.pass = pass;
}
#Override
public String loadDataFromNetwork() throws RestClientException {
String client_id = "testid";
String client_secret = "testsecret";
HttpBasicAuthentication authHeader = new HttpBasicAuthentication(client_id, client_secret);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setUserAgent("AndroidNotesApp/1.0");
String data = String.format("grant_type=password&username=%s&password=%s", this.user, this.pass);
HttpEntity<String> requestEntity = new HttpEntity<>(data, requestHeaders);
String url = "http://10.0.2.2:8000/oauth2/token/";
return getRestTemplate().postForObject(url, requestEntity, String.class);
}
}
The SpiceManager in my activity is declared like:
protected SpiceManager spiceManager = new SpiceManager(JacksonSpringAndroidSpiceService.class);
and the request is made by the following lines:
OAuth2Request req = new OAuth2Request(user, pass);
spiceManager.execute(req, new OAuth2RequestListener());
user and pass are Strings, which get their values from EditText views.
But when I try to run this request, I get an exception 400 BAD REQUEST.
I set up logging in my django app to print the requests which come to /oauth2/token/, and I see, that POST parameters are empty in this request (I expect them to be the same as during the curl request, something like {'grant_type': 'password', 'password': 'pass', 'username': 'user'}).
Why are POST parameters empty in case of RoboSpice request? What am I doing wrong?
P.S. Just in case: the oauth2 authentication in my django web application is implemented using DjangoOAuthToolkit with DjangoRestFramework.
UPDATE: I decided to setup nginx proxy before my django web application to log the request body. The request body I get from the Android app is the following:
\x22grant_type=password&username=user&password=pass\x22
So the strange \x22 symbol is added in the beginning and in the end of the body (I believe it is a double-quote " symbol, but I'm not sure). Seems that these \x22 screw up POST parameter parsing in django. How can I get rid of these symbols?
I managed to solve my problem, so I'm posting an answer in case it helps someone.
SpringAndroidSpiceRequest by default tries to map a java object into JSON, so when I tried to send a String in request body, it wrapped it in double quotes to make it a JSON string. I don't need to send a request body as a JSON string, and in order to do that I need to define additional message converters.
Strangely, these lines in constructor don't seem to do anything
setRestTemplate(new RestTemplate());
getRestTemplate().getMessageConverters().add(new StringHttpMessageConverter());
When I used debugger, it showed just one message converter, MappingJacksonHttpMessageConverter. So I decided to add my own message converters in loadDataFromNetwork method.
I needed two message converters: FormHttpMessageConverter, which will process request and make a request POST body from MultiValueMap, and MappingJacksonHttpMessageConverter, which will process the JSON response into OAuth2Token POJO, which I also declared.
I believe, that for simple testing of REST API with client (POST plain strings and receive plain strings) it'll be better to choose another implementation for SpiceRequest other than SpringAndroidSpiceRequest, but I decided to stick with it, as it'll be easier to implement the complete client for my web application.
The complete code for OAuth2Request:
public class OAuth2Request extends SpringAndroidSpiceRequest<OAuth2Token> {
private final String user;
private final String pass;
public OAuth2Request(String user, String pass) {
super(OAuth2Token.class);
this.user = user;
this.pass = pass;
}
#Override
public OAuth2Token loadDataFromNetwork() throws RestClientException {
String client_id = "testid";
String client_secret = "testsecret";
HttpBasicAuthentication authHeader = new HttpBasicAuthentication(client_id, client_secret);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setUserAgent("AndroidNotesApp/1.0");
MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
data.add("grant_type", "password");
data.add("username", this.user);
data.add("password", this.pass);
HttpEntity<?> requestEntity = new HttpEntity<>(data, requestHeaders);
String url = "http://10.0.2.2:8000/oauth2/token/";
getRestTemplate().getMessageConverters().clear();
getRestTemplate().getMessageConverters().add(new FormHttpMessageConverter());
getRestTemplate().getMessageConverters().add(new MappingJacksonHttpMessageConverter());
return getRestTemplate().postForObject(url, requestEntity, OAuth2Token.class);
}
}

how to pass parameters to RESTlet webservice from android?

I've been looking online for how to pass parameters to RESTlet webservice but it seem there are not much tutorial concerning RESTlet.
I would like to send some parameters gathered from a form on my android application (it would be great if i could do this using JSON).
well i solved this
as for the server side
#Post
public JSONArray serverSideFunction(Representation entity)
throws JSONException {
try {
JSONObject req = (new JsonRepresentation(entity)).getJsonObject();
System.out.println(req.getString(/* filed name */));
System.out.println(req.getString(/* filed name */));
/*
* you can retrieve all the fields here
* and make all necessary actions
*/
} catch (IOException e) {
e.printStackTrace();
}
}
as for the Android Side
HttpClient client = new DefaultHttpClient();
String responseBody;
JSONObject jsonObject = new JSONObject();
try{
HttpPost post = new HttpPost(WebService_URL);
jsonObject.put("field1", ".........");
jsonObject.put("field2", ".........");
StringEntity se = new StringEntity(jsonObject.toString());
post.setEntity(se);
post.setHeader(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
post.setHeader("Content-type", "application/json");
Log.e("webservice request","executing");
ResponseHandler responseHandler = new BasicResponseHandler();
responseBody = client.execute(post, responseHandler);
/*
* You can work here on your responseBody
* if it's a simple String or XML/JSON response
*/
}
catch (Exception e) {
e.printStackTrace();
}
I hope this may be of help
In fact, it depends on what you want to do. With REST (see http://en.wikipedia.org/wiki/Representational_state_transfer), there are two ways to pass parameters or data. Before you need to understand some concepts:
Resource: the REST entity by itself.
Representation: corresponds to its state and can be gotten or updated using different HTTP methods. The kind of content is identified using the content type header (media type in Restlet).
Methods: the GET method is used to get the resource state, PUT to update it, POST to create a new resource and specify its state the same time, DELETE to delete a resource.
Restlet provides Java entities for REST elements.
So, after described that, you can see that passing data or parameters depends of your use case:
1°) Do you want to update the resource state? In this case, you will use the content of the request with methods like POST or PUT. The data structure is free from text, JSON, XML or binary... Restlet provides the ClientResource class to execute requests on RESTful applications. It also provides support to build the representation to send and extract data from the one received. In this case, your data gathered from a form will be used to build the representation. Here are some samples:
//Samples for POST / PUT
ClientResource cr = new ClientResource("http://...");
cr.post(new StringRepresentation("test"));
MyBean bean = new MyBean();
(...)
//Jackson is a tool for JSON format
JacksonRepresentation<MyBean> repr
= new JacksonRepresentation<MyBean>(bean);
cr.put(repr);
//Samples for GET
Representation repr1 = cr.get();
bean = (new JacksonRepresentation<MyBean>(repr1, MyBean.class)).getObject();
2°) Do you want to specify parameters on your GET requests (for example to configure data to retreive and so on)? In this case, you can simply add it on the ClientResource, as described below:
ClientResource cr = new ClientResource("http://...");
cr.getReference().addQueryParameter("q", "restlet");
Representation repr = cr.get();
In this case, your data gathered from a form will be used to build the parameters.
Hope it helps you.
Thierry
If you want request with json structure and your response as JSONObject maybe you can do like this in server side:
public class RequestJSON extends ServerRecource{
#Post("json")
public JSONObject testRequest(String entity){
JSONObject request = new JSONObject(entity);
String value1 = request.getString("key1");
int value2 = request.getInt("key2");
return /* your JSONObject response */;
}
}
And your request can be :
{"key1":"value1", "key2":value2}
I hope this can help you

How to consume Session dependent WCF services using Ksoap2-Android

I am using Ksoap2-Android for consuming the WCF Services.
For the dotnet client we keep the allowCookies="true" in our binding configuration and it sends the same sessionid and keeps my sessions intact in my WCF services (My services are
interdependent and use the sessions).
Any one know any such setting for ksoap2-android, that will allow me to consume the
WCF service keeping my session intact on the server.
Currently when i make a new call to the service, the sessionid gets changed and all my
session variables clear out and loose their values.
In C# i do the next, just use the android methods to do this:
1.- Make the Http request,
2.- Make a Cookie Container of the first request.
3.- Put the cookieContainer over the second request, for example you can put in a bundle in a intent for the 2nd activity, and use this cookies for send the second http request...
My C# Code;
protected static void GetData()
{
CookieContainer cookies = new CookieContainer();
HttpWebRequest request1 = (HttpWebRequest)WebRequest.Create("https://any.com/url");
request1.CookieContainer = cookies;
HttpWebResponse response1 = (HttpWebResponse)request1.GetResponse();
StreamReader responseReader1 = new StreamReader(response1.GetResponseStream());
Response1 = responseReader1.ReadToEnd();
responseReader1.Close();
responseReader1.Dispose();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
request.CookieContainer = cookies;
request.Method = "GET";
request1.KeepAlive = true;
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader responseReader = new StreamReader(response.GetResponseStream());
Response = responseReader.ReadToEnd();
responseReader.Close();
responseReader.Dispose();
if (Response.Contains("Server Error in '/Verification' Application."))
{
Console.WriteLine("Empty Registry" + Url);
}
}
catch (WebException ex)
{
if (ex.Response != null)
{
Console.WriteLine("Failed at: " + Url);
}
if (ex.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine(ex.Status);
}
}
else if (ex.Status == WebExceptionStatus.NameResolutionFailure)
{
Console.WriteLine(ex.Status);
}
}
}
I do That for keep the sesionID of the first request, and later, in the second request, i add the cookieContainer (because the server requires me) (to make a bot search) ;)... hope this give you ideas.

Categories

Resources