4

I know a similar question has been asked a few times now, but I can't seem to find a solution for a simple problem: PATCH verb.

So I ask anyone who as solved the PATCH issue in Android either by using OkHttp, Volley or Retrofit ( or if you used a different method or library, please share).

I have spent 3 days digging around and I can't find any solutions to this problem. All I am trying to do is to do a partial update to a table via RESTful API which is built and ran in .NET environment.

I am currently using Volley, but every time I run the app, I get the exception error that says something along the lines of PATCH is not an allowed...? and it then gives a array of other verbs that can be used such as "POST, PUT, GET...". So, not very helpful at all.

I have tried using OkHttp like this:

  JSONObject infoRequestBody = new JSONObject();


    try {


        infoRequestBody.put("Salutation", "MRrr.");
        infoRequestBody.put("FirstName", "Work Now!!");
        infoRequestBody.put("LastName", "Test from my mobile again!! Okhttp");
        infoRequestBody.put("MiddleName", "From the Mobile");
        infoRequestBody.put("NickName", null);
        infoRequestBody.put("BirthDate", "1978-11-23T00:00:00.000Z");
        infoRequestBody.put("CompanyName", null);
        // infoRequestBody.put("RegDate", "2015-01-18T23:39:13.873Z");
        infoRequestBody.put("Gender", "Male");
        infoRequestBody.put("IsPregnant", null);
        infoRequestBody.put("IsNursing", null);
        infoRequestBody.put("WorkPhone", null);
        infoRequestBody.put("WorkPhoneExt", null);
        infoRequestBody.put("PhoneNumber", null);
        infoRequestBody.put("Address", "My android app working!!");
        infoRequestBody.put("Address2", null);
        infoRequestBody.put("City", null);
        infoRequestBody.put("State", "CA");
        infoRequestBody.put("ZipCode", null);
        infoRequestBody.put("EmailAddress", "email@me.com");
        infoRequestBody.put("Occupation", null);
        infoRequestBody.put("CountryofResidence", "United States");
        infoRequestBody.put("CountryCode", "US");
        infoRequestBody.put("Ethnicity", "Asian");
        infoRequestBody.put("ModifiedDate", "2015-01-18T23:39:13.873Z");
        infoRequestBody.put("ModifiedBy", null);
        infoRequestBody.put("AcceptedTerms", true);
        infoRequestBody.put("AcceptedPrivacyPolicyCheckbox", false);
        infoRequestBody.put("AcceptedUnder18ConsentCheckbox", false);
    } catch (JSONException e1) {
        e1.printStackTrace();
    }
  RequestBody body = com.squareup.okhttp.RequestBody.create(JSON, infoRequestBody.toString());


    com.squareup.okhttp.Request.Builder request = new com.squareup.okhttp.Request.Builder()
             .url("APIUrls.PATIENT_INFO_URL")
            .addHeader("Content-Type", "application/json")
            .addHeader("Accept", "application/json")
            .addHeader("X-ZUMO-APPLICATION", APIUrls.ZUMO_KEY)
            .patch(body);



    client.newCall(request).enqueue(new Callback() {




        @Override
        public void onFailure(com.squareup.okhttp.Request request, IOException e) {


        }

        @Override
        public void onResponse(com.squareup.okhttp.Response response) throws IOException {

            if (!response.isSuccessful()) throw new IOException("Unexpected code" + response);

            Headers responseHeaders = response.headers();
            for (int i = 0; i < responseHeaders.size(); i++) {
                System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
            }


            System.out.println(response.body().string());

        }
    });

but client.newCall(request).enque(new Callback()... gives me an error saying that the request can not be applied to Request.Builder.

I am new to OkHttp, by the way.

Anyways, I have been struggling to get PATCH to work for far too long. Maybe I am shooting in the dark, but if anyone out there has any suggestions, thoughts on how to get 'PATCH` to work, I'd be forever grateful to you.

Thanks for your help.

GoBusto
  • 4,632
  • 6
  • 28
  • 45
ito
  • 154
  • 3
  • 15
  • You're missing a call to `.build()` after `.patch()`. You want a `Request`, not a `Request.Builder`. – Jesse Wilson Jan 27 '15 at 05:20
  • Have you tried using a REST client like Rested, one of the many chrome extensions, etc, to make sure the request is supported by your server? – Jacob Tabak Jan 27 '15 at 06:51
  • @JacobTabak, yes I have. It all works fine, except when I try to do the same in Android. – ito Jan 27 '15 at 18:06

3 Answers3

4

As GreyBeardedGeek says, you can use Retrofit + OkHttp to implement this.

RestAdapter restAdapter = new RestAdapter.Builder()
                .setClient(new OkClient())
                .setEndpoint(ENDPOINT)
                .build();
MyPatchService patchService = restAdapter.create(MyPatchService.class);
UserUpdate updatedUser = new UserUpdate();
updatedUser.Salutation = "Mr";
updatedUser.FirstName = "Adam";
patchService.update(updatedUser);

And MyPatchService looks something like this:

public interface MyPatchService {
    @PATCH("/some/update/endpoint")
    User update(UserUpdate updatedObject);
}

Your UserUpdate class could look like this:

public class UserUpdate {
    public String Salutation;
    public String FirstName;
    public String LastName;
}

If you do this, the JSON that will be sent will look like this (note that LastName is not serialized since it was never set - this is configurable through supplying a custom Gson instance to Retrofit):

{
  "Salutation": "Mr",
  "FirstName": "Adam"
}

The request/response objects are plain Java representations of the objects you send/receive over the network. Being a patch request, you can make your member vars Integer, Boolean, etc and they won't get serialized if not set (i.e. if they're null).

Adam S
  • 16,144
  • 6
  • 54
  • 81
  • Thanks @Adam and @GreyBeardedGeek for your responses. So, the JSON object that I am sending as a PATCH, will it be added in the restAdapter object? Like so `.setPatch(myJsonData)`, or will it be passed inside the interface? Sorry for the questions: I am new to OkHTTP and Retrofit. Thanks – ito Jan 27 '15 at 05:04
  • You simply create the request/response objects then write the interface to expect them - Retrofit does all the serialization and deserialization for you. I'll update the example a little further, but I'd recommend [having a good read about the magic that is Retrofit](http://square.github.io/retrofit/). – Adam S Jan 27 '15 at 15:14
  • thanks for your help. I am now implementing all you have showed here. One thing: when you said `User update(UserUpdate updatedObject);` did you mean to say `Userupdate(UserUpdate updatedObject);` `(UserUpdated(UserUpdated updatedObject);` Correct? – ito Jan 27 '15 at 18:55
  • Nope, it's written as intended - in this example the server returns the full updated object (in this case, a `User` object). I _do not know_ if this is correct behaviour for an endpoint accepting requests with the PATCH verb. – Adam S Jan 27 '15 at 19:01
  • Ok. My endpoint is going to be `/tables/PatientInformation/my-table-id-here"`, so I am assuming than I would need to have something along these lines: ` public interface MyPatchService { @PATCH("/tables/PatientInformation/my-table-id-here"") User PatientInformation(UserUpdate updatedObject); }`. Is this what you're saying? I am very sorry if this is too noobie. – ito Jan 27 '15 at 19:14
  • That's going down the path of explaining how Retrofit works in general, rather than how it works with PATCH. Read the docs; they're short, cover all of this in detail (for example how to specify the ID in that request endpoint via the interface) and do a far better job of explaining the library than getting it piecemeal from me :) – Adam S Jan 27 '15 at 19:21
  • 1
    Thanks @Adam. Will do. Thanks again, and will make sure to report if/when I get this thing to work :) – ito Jan 27 '15 at 19:54
1

I haven't tried this, but apparently, if you use Retrofit with the OkHTTP's OKClient, then Retrofit supports an @Patch annotation.

See http://square.github.io/retrofit/javadoc/retrofit/http/PATCH.html and Retrofit & HTTP Patch

Community
  • 1
  • 1
GreyBeardedGeek
  • 29,460
  • 2
  • 47
  • 67
  • 1
    As of March 2016, with Retrofit 2.0.0-beta4 I am using PATCH successfully without any further libraries inclusion as mentioned in the answers here. – ahmadalibaloch Mar 16 '16 at 08:19
1

Alright! Thanks @Adam and everyone who contributed to get me in the right direction. Really appreciate it. So, here's what I ended up doing (I barely customized what @Adam posted, which of course is the correct answer!):

Firstly,

I set up my UserUpdate class as follows:

public class UserUpdate {
public String Salutation;
public String FirstName;
public String LastName;
public String MiddleName;
public String NickName;

     //Setters and Getters...
  public String getSalutation() {
      return Salutation;
  }

  public void setSalutation(String salutation) {
      Salutation = salutation;
  }

  public String getFirstName() {
     return FirstName;
  }

  public void setFirstName(String firstName) {
     FirstName = firstName;
  }

  public String getLastName() {
      return LastName;
  }

  public void setLastName(String lastName) {
      LastName = lastName;
  }

public String getMiddleName() {
    return MiddleName;
}

public void setMiddleName(String middleName) {
    MiddleName = middleName;
}

public String getNickName() {
    return NickName;
}

 public void setNickName(String nickName) {
     NickName = nickName;
 }


}

and then I set up the interface class called PatientInfoAPI:

public interface PatientInfoAPI{

@PATCH("/tables/PatientInformation/{id}")
  public void update(@Body UserUpdate updatedDAta, @Path("id") String  id,Callback<UserUpdate> response);


 }

Notice I'm passing the table id to be "PATCH'd", and most importantly I have the @Body annotation which allows me to pass the data to the update without getting an error (I tried without it and was getting an error)

And finally inside my main Activity:

 RestAdapter restAdapter = new RestAdapter.Builder()
            .setClient(new OkClient()) //very important!!!
            .setEndpoint(ENDPOINT)//base url
            .setRequestInterceptor(new RequestInterceptor() {//You may not need to pass headers, but I do, so this is how it's done.
                @Override
                public void intercept(RequestFacade request) {

                    request.addHeader("Content-Type", "application/json");
                    request.addHeader("Accept", "application/json");
                    request.addHeader("X-ZUMO-APPLICATION", APIUrls.ZUMO_KEY);

                }
            })
            .build();
    PatientInfoAPI patchService = restAdapter.create(PatientInfoAPI.class);

    UserUpdate updatedUser = new UserUpdate();
    updatedUser.setSalutation("Sr.");
    updatedUser.setFirstName("YesSirtItsWorking");
    updatedUser.setLastName("Bays");
    updatedUser.setMiddleName("ComeOn Now man!");
    updatedUser.setNickName("");

     //Passing the tableId along with the data to update
     patchService.update(updatedUser, APIUrls.TABLE_ID, new retrofit.Callback<UserUpdate>() {
        @Override
        public void success(UserUpdate userUpdates, retrofit.client.Response response) {


            Log.v("All is good", "Nickname:" +  userUpdates.getNickName().toString() + " Email: " + userUpdates.getEmailAddress().toString());



        }

        @Override
        public void failure(RetrofitError error) {

            Log.v("All is good", error.getLocalizedMessage().toString());


        }
    });

I know that maybe some of the steps are not very sophisticated, but for those who are starting with Retrofit and OkHttp, and most importantly have been struggling to get PATCH to work with Android, this is the solution. Again, props to @Adam for pretty much guide me through the process. @Adam was the one who really answered this question - I'm just putting it all together for those who may need a to see the full picture. Thanks, @Adam!!! You are a rockstar!

ito
  • 154
  • 3
  • 15