SlideShare a Scribd company logo
1 of 47
Download to read offline
REST Drupal
         Talking to Drupal from Javascript, iOS and Android




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   1

Sunday, November 18, 12
About me                                         I pity the fool
                                                         who doesn’t use
                                                              Drupal
         Hi, I’m Alexandru Badiu.
         I’m a software engineer, amateur game
         developer and part time Mister T
         impersonator.

         Drupal user for 9 years, board member in
         Drupal Romania.

         I work for Demotix.

         Twitter @voidberg
         Web ctrlz.ro




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal               2

Sunday, November 18, 12
REST Drupal


         Mobile is pretty big now days
         Drupal 8 Web services initiative
         For Drupal 7 there’s the Services module




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   3

Sunday, November 18, 12
YASS



         Oh boy, yet another Services session!




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   4

Sunday, November 18, 12
Nope




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   5

Sunday, November 18, 12
In this session
         Doing “web services” without Services
         Doing “web services” without Drupal’s slowness
         Using Services
             ... With Javascript
             ... With Objective-C
             ... With Java
         Uploading files and cursing at Apple and Google
         Some memes

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   6

Sunday, November 18, 12
Sorry :(




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   7

Sunday, November 18, 12
1
                 Services without
                     services
         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   8

Sunday, November 18, 12
Why would you do that?




                                      One word: Drupal is slow



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal    9

Sunday, November 18, 12
Services without services

                                                            Sometimes you really don’t need services

                                                            Getting a list of news and showing some details
                                                            about them.

                                                            Showing nearby businesses on a map.

                                                            Users submitting anonymous tips or comments.

                                                            Use JSON and don’t shoot yourself in the foot
                                                            by using XML or plists.




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                            10

Sunday, November 18, 12
However
          Services is not the slowest thing, Drupal is
          Shameless plug: Drupal NoBootstrap
          Small php library which allows you to be able to use some of
          Drupal's functions without bootstrapping Drupal.
          Used it for Solr powered instant Google like map of businesses
          Used it for lightning fast autocomplete
          Used it for coupon claiming app
          https://github.com/voidberg/Drupal-NoBootstrap


         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal              11

Sunday, November 18, 12
2
                                     Services

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   12

Sunday, November 18, 12
Services

          A long time ago in a Drupalcamp far, far away
          Last year I was complaining about Services
          Clunky way of connecting (session, nonce, timestamp)
          Inconsistent responses
          Bugs in json_server




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal    13

Sunday, November 18, 12
Services 3

          Services 3 is much much nicer
          REST based
          No more nonce and other crap
          Session is kept in the HTTP calls
          Consistent responses




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   14

Sunday, November 18, 12
Services 3
          REST in a nutshell

          Resource based

          HTTP based

          CRUD

               GET /endpoint/resource/id

               POST /endpoint/resource + (post data)

               DELETE /endpoint/resource/id

               PUT /endpoint/resource/id + (post data)

               GET /endpoint/resource



          GET /services/comment?parameters[nid]=123&parameters[timestamp]=123456:123600


         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                             15

Sunday, November 18, 12
Services 3
          REST in a nutshell
          Actions
               Do not target a specific resource
               POST /services/apachesolr/reindex
          Targeted actions
               Target a specific resource
               POST /services/node/123/unpublish
          Relationships
               GET /services/node/123/comments

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   16

Sunday, November 18, 12
3
                              Drupanium

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   17

Sunday, November 18, 12
Drupanium
          Distribution targeted at building mobile apps
          For Titanium Studio
          Should be possible to use with Cordova
          Looks abandoned
          Simple js code to grab
          Decent list of core services resources
          http://drupanium.org/api



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   18

Sunday, November 18, 12
4
                                                   iOS

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   19

Sunday, November 18, 12
Android
          Drupal iOS SDK
          The library formerly known as KBDrupalConnect
          Uses AFNetworking
          Implements all core service resources
          Can use OAuth
          Uses blocks
          https://github.com/workhabitinc/drupal-ios-sdk



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   20

Sunday, November 18, 12
Drupal iOS SDK
          Copy AFNetworking code and Drupal iOS SDK code in project
          Update Settings.m with url, endpoint etc
          Profit!




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal         21

Sunday, November 18, 12
Drupal iOS SDK
       DIOSUser *user = [[DIOSUser alloc] init];

       [user userLoginWithUsername:username andPassword:password
          success:^(AFHTTPRequestOperation *operation, id responseObject) {
               // Login ok
               DIOSSession *session = [DIOSSession sharedSession];
               [session setUser:[responseObject objectForKey:@"user"]];
          }
          failure:^(AFHTTPRequestOperation *operation, NSError *error) {
               // Login failed
               [MBProgressHUD hideHUDForView:self.view animated:YES];
          }
       ];




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                 22

Sunday, November 18, 12
Drupal iOS SDK
          DIOSNode *node = [[DIOSNode alloc] init];

          NSMutableDictionary *nodeData = [NSMutableDictionary new];
          [nodeData setValue:[@"Node title" text] forKey:@"title"];

          NSDictionary *bodyValues = [NSDictionary dictionaryWithObjects:[NSArray
          arrayWithObjects:@"Node title", nil] forKeys:[NSArray arrayWithObjects:@"value", nil]];
          NSDictionary *languageDict = [NSDictionary dictionaryWithObject:[NSArray
          arrayWithObject:bodyValues] forKey:@"und"];
          [nodeData setValue:languageDict forKey:@"body"];

          NSDictionary *fieldValue = [NSDictionary dictionaryWithObjects:[NSArray
          arrayWithObjects:@"Field value", nil] forKeys:[NSArray arrayWithObjects:@"value", nil]];
          NSDictionary *fieldLangDict = [NSDictionary dictionaryWithObject:[NSArray
          arrayWithObject:sessionValue] forKey:@"und"];
          [nodeData setValue:fieldLangDict forKey:@"field_foo_bar"];
          [nodeData setValue:@"nodetype" forKey:@"type"];
          [nodeData setValue:@"und" forKey:@"language"];
          [node nodeSave:nodeData success:^(AFHTTPRequestOperation *operation, id responseObject) {
              // Node was saved
          } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
              // Node save failed
          }];




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                         23

Sunday, November 18, 12
Extending it
          @interface DemotixServices: NSObject
          - (void)userRegister:(NSDictionary *)user
                   success:(void (^)(AFHTTPRequestOperation *operation, id responseObject)) success
                   failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error)) failure;
          @end



         #import "DemotixServices.h"
         #import "DIOSSession.h"

         @implementation DemotixServices

         #pragma mark userRegister
         - (void)userRegister:(NSDictionary *)user
                  success:(void (^)(AFHTTPRequestOperation *operation, id responseObject)) success
                  failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error)) failure {
           [[DIOSSession sharedSession] postPath:[NSString stringWithFormat:@"%@/%@", kDiosEndpoint,
         @"demotix_services_user/register"]
                                      parameters:user
                                      success:success
                                      failure:failure];
         }
         @end;



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                          24

Sunday, November 18, 12
Saving the session
          // After a login
          NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
          NSArray *cookies = [cookieStorage cookies];
          for (NSHTTPCookie *cookie in cookies) {
            if ([cookie.name hasPrefix:@"SESS"]) {
              [keychainS setObject:cookie.name forKey:(__bridge id)kSecAttrAccount];
              [keychainS setObject:cookie.value forKey:(__bridge id)kSecValueData];
            }
          }

          // On next app start
          NSString *sessionName = [keychainS objectForKey:(__bridge id)kSecAttrAccount];
          NSString *sessionValue = [keychainS objectForKey:(__bridge id)kSecValueData];

          if (![sessionName isEqualToString:@""] && ![sessionValue isEqualToString:@""]) {
            DIOSSession *session = [DIOSSession sharedSession];
            [session setDefaultHeader:@"Cookie" value:[NSString stringWithFormat:@"%@=%@", sessionName,
          sessionValue]];

              DIOSSystem *system = [[DIOSSystem alloc] init];
              [system systemConnectwithSuccess: ^(AFHTTPRequestOperation *operation, id responseObject) {
              }
              failure:^(AFHTTPRequestOperation *operation, NSError *error) {
              }];
          }




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                          25

Sunday, November 18, 12
File upload
          NSMutableURLRequest *request = [[DIOSSession sharedSession]
              multipartFormRequestWithMethod:@"POST"
              path:[NSString stringWithFormat:@"%@/%@", kDiosEndpoint, @"demotix_services_story/upload"]
              parameters:params
              constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) {
                    NSError *error;
                    if (![formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"joined"
          error:&error]) {
                        NSLog(@"An error occured: %@", error);
                    }
                    else {
                        NSLog(@"No error");
                    }
          }];

          AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
          [operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long
          long totalBytesExpectedToWrite) {
              [delegate updateUploadProgress:totalBytesWritten from:totalBytesExpectedToWrite];
          }];

          [operation setCompletionBlockWithSuccess:success failure:failure];
          [operation start];




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                        26

Sunday, November 18, 12
File upload




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   27

Sunday, November 18, 12
File upload
          AFNetworking uses the iOS API for http calls
          The API has a bug since iOS 3
          When uploading files on 3G you get a OOM error
          Solution is to throttle the upload which the API has no support for
          AFNetworking is still working on this
          I ended up using ASIHttpRequest just for file upload with throttled
          upload on 3G



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                   28

Sunday, November 18, 12
File upload
       [ASIHTTPRequest setShouldThrottleBandwidthForWWAN:YES];
       [ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount*throttle_amount];

       NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%@", kDiosBaseUrl,
       kDiosEndpoint, @"demotix_services_story/upload"]];
       __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];

       [request setCompletionBlock:^{
           success(responseString);
       }];
       [request setFailedBlock:^{
           failure(responseString, error);
       }];

       for (NSString * fkey in files) {
           NSDictionary *file = [files objectForKey:fkey];
           NSString *fileName = [file valueForKey:@"name"];

              [request setFile:[file valueForKey:@"fileUrl"] forKey:fileName];
       }
       [request      setShouldContinueWhenAppEntersBackground:YES];
       [request      setTimeOutSeconds:999];
       [request      setUploadProgressDelegate:self];
       [request      startAsynchronous];




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                      29

Sunday, November 18, 12
5
                                      Android

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   30

Sunday, November 18, 12
Android
          DrupalCloud is not working anymore
          Hasn’t been updated to Services 3
          Never really liked the architecture
          Dandy might work, I was scared
          No documentation at all
          Source code seems over engineered
          Not sure it works with Services 3 (issue queue says it doesn’t)



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal               31

Sunday, November 18, 12
Android


        So I built my own
        Android Drupal SDK
        https://github.com/voidberg/
        Android-Drupal-Sdk




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   32

Sunday, November 18, 12
First step
          Let’s choose a HTTP Client
          It’s a mess
          HttpUrlConnection
               Been since the dark ages
               Had some serious bugs before Froyo
               Still behind the apache lib
          Apache HTTP Client
               Best option so far IMHO
               Has been deprecated

         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   33

Sunday, November 18, 12
We have a winner
          Android Asynchronous Http Client

          http://loopj.com/android-async-http/

          You’ll be in select company - Instagram

          HTTP requests happen outside the UI thread

          Requests use a threadpool to cap concurrent resource usage

          Multipart file uploads with no additional third party libraries

          Automatic smart request retries optimized for spotty mobile connections

          Automatic gzip response decoding support for super-fast requests

          Built-in response parsing into JSON with JsonHttpResponseHandler

          Persistent cookie store, saves cookies into your app’s SharedPreferences

          Uses the Apache HTTP Client


         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                        34

Sunday, November 18, 12
Using it

        ServicesClient client;
        client = new ServicesClient("http://www.example.com", "api/
        mobile");

        client.setBasicAuth("username","password/token");

        PersistentCookieStore cookieStore;
        cookieStore = new PersistentCookieStore(this);
        client.setCookieStore(cookieStore);




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal         35

Sunday, November 18, 12
Using it
          SystemServices ss;
          ss = new SystemServices(client);

          JsonHttpResponseHandler connectHandler = new JsonHttpResponseHandler() {
              @Override
              public void onSuccess(JSONObject response) {
                 JSONObject user = response.getJSONObject("user");
                  int uid = user.getInt("uid");
              }

                 @Override
                 public void onFailure(Throwable e, JSONObject response) {
                     // System.Connect call failed
                 }

                 @Override
                 public void onFinish() {
                 }
          };

          ss.Connect(connectHandler);



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                        36

Sunday, November 18, 12
Using it
          UserServices us;

          us = new UserServices(client);

          JsonHttpResponseHandler loginHandler = new JsonHttpResponseHandler() {
              @Override
              public void onSuccess(JSONObject response) {
                  boolean error = false;
                  JSONObject user = response.getJSONObject("user");
              }

                @Override
                public void onFailure(Throwable e, JSONObject response) {
                    // Username or password were incorrect
                }

                @Override
                public void onFinish() {
                    activity.hideProgressDialog();
                }
          };

          activity.showProgressDialog("Logging you in");
          us.Login("username", "password");




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                      37

Sunday, November 18, 12
Extending it
         public class DemotixServices {
           private ServicesClient client;

             public DemotixServices(ServicesClient c) {
               client = c;
             }

           public void Register(String fname, String lname, String name, String email, String
         password, AsyncHttpResponseHandler responseHandler) {
             JSONObject params = new JSONObject();
             try {
               params.put("name", name);
               params.put("mail", email);
               params.put("pass", password);
               params.put("field_firstname", fname);
               params.put("field_lastname", lname);
               params.put("legal_accept", "1");
               params.put("from_mobile", "1");
             } catch (JSONException e) {
               e.printStackTrace();
             }

                 client.post("demotix_services_user/register", params, responseHandler);
             }
         }



         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                   38

Sunday, November 18, 12
File upload

          InputStream myInputStream = blah;
          RequestParams params = new RequestParams();
          params.put("secret_passwords", myInputStream, "passwords.txt");

          File myFile = new File("/path/to/file.png");
          RequestParams params = new RequestParams();
          try {
              params.put("profile_picture", myFile);
          } catch(FileNotFoundException e) {}

          client.post(...)




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal               39

Sunday, November 18, 12
File upload




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   40

Sunday, November 18, 12
File upload

          Turns out that the library tries to read all data before uploading
          For large files this is a problem as you’ll run out of memory
          immediately
          Solution: go low level and build the multi part yourself by
          attaching FileInputStreams




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                  41

Sunday, November 18, 12
File upload
         HttpParams httpParams = new BasicHttpParams();
         HttpConnectionParams.setConnectionTimeout(httpParams, 900 * 1000);
         HttpConnectionParams.setSoTimeout(httpParams, 900 * 1000);

         HttpClient httpclient = ServicesClient.client.getHttpClient();
         HttpContext httpContext = ServicesClient.client.getHttpContext();

         HttpPost httppost = new HttpPost("http://www.example.com/api/mobile/service/upload");
         httppost.setParams(httpParams);

         MultipartEntity entity = new MultipartEntity();
         // Add parameters to the post body
         entity.addPart("param1", new StringBody(param1.toString(),"application/json",
         Charset.forName("UTF-8")));

         // Add file to post body
         InputStream istream1 = new FileInputStream("file1.jpg");
         entity.addPart("file1", new InputStreamBody(istream1, "file1"));

         httppost.setEntity(entity);
         HttpResponse response = httpclient.execute(httppost, httpContext);




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                    42

Sunday, November 18, 12
File upload




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   43

Sunday, November 18, 12
File upload

          Nope
          There is no easy way of getting the progress of an upload
          We fake it: use CustomMultiPartEntity instead of MultiPartEntity
          This class notifies a listener of the bytes read so far
          Calculate an approximation of the size of the post data




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                44

Sunday, November 18, 12
File upload
          CustomMultiPartEntity postEntity = new CustomMultiPartEntity(new
          CustomMultiPartEntity.ProgressListener() {
              @Override
              public void transferred(long num) {
                  updateUploadProgress((int) ((num / (float) totalUploadSize) * 100));
              }
          });

          try {
              postEntity.addPart("param1", new StringBody(param1.toString(),"application/json",
          Charset.forName("UTF-8")));
              totalUploadSize = postEntity.getContentLength();
              for (Map.Entry<String, String> entry : files.entrySet()) {
                  try {
                      InputStream istream = new FileInputStream(entry.getValue());
                      postEntity.addPart(entry.getKey(), new InputStreamBody(istream,
          entry.getKey()));
                      totalUploadSize += new File(entry.getValue()).length();
                  } catch (FileNotFoundException e) {}
              }
              httppost.setEntity(postEntity);
          }
          catch (UnsupportedEncodingException e) {}

          HttpResponse response = httpclient.execute(httppost, httpContext);


         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal                                     45

Sunday, November 18, 12
Another caveat

          Varnish has issues by default
          You have a rule that does “return post” for, well, POST calls
          Upload will fail with a socket exception
          Add another rule that does “return stream” for POST calls made
          to your upload url




         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal              46

Sunday, November 18, 12
Thanks!
         Questions?


          Alexandru Badiu.
          Twitter @voidberg
          Web http://ctrlz.ro

          Email andu@ctrlz.ro

          D.O http://drupal.org/user/8662


         Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal   47

Sunday, November 18, 12

More Related Content

Similar to REST Drupal

Cloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloudCloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloudAlexandru Badiu
 
Headless Drupal en pratique
Headless Drupal en pratiqueHeadless Drupal en pratique
Headless Drupal en pratiqueSimon Morvan
 
RESTful OSGi middleware for NoSQL databases with Docker
RESTful OSGi middleware for NoSQL databases with DockerRESTful OSGi middleware for NoSQL databases with Docker
RESTful OSGi middleware for NoSQL databases with DockerBertrand Delacretaz
 
Using Composer with Drupal and Drush
Using Composer with Drupal and DrushUsing Composer with Drupal and Drush
Using Composer with Drupal and DrushPantheon
 
Getting Started with DrupalGap
Getting Started with DrupalGapGetting Started with DrupalGap
Getting Started with DrupalGapAlex S
 
Building businesspost.ie using Node.js
Building businesspost.ie using Node.jsBuilding businesspost.ie using Node.js
Building businesspost.ie using Node.jsRichard Rodger
 
Parallel Query on Exadata
Parallel Query on ExadataParallel Query on Exadata
Parallel Query on ExadataEnkitec
 
Aris 9 See the Future Today
Aris 9 See the Future TodayAris 9 See the Future Today
Aris 9 See the Future TodaySoftware AG
 
Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...
Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...
Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...Uwe Printz
 
Custom Android Code Templates
Custom Android Code TemplatesCustom Android Code Templates
Custom Android Code Templatesmurphonic
 
Headless Drupal: A modern approach to (micro)services and APIs
Headless Drupal: A modern approach to (micro)services and APIsHeadless Drupal: A modern approach to (micro)services and APIs
Headless Drupal: A modern approach to (micro)services and APIssparkfabrik
 
Drupal
DrupalDrupal
Drupalbtopro
 
Headless Drupal
Headless DrupalHeadless Drupal
Headless Drupaldrubb
 
CouchDB and Rails on the Cloud
CouchDB and Rails on the CloudCouchDB and Rails on the Cloud
CouchDB and Rails on the Cloudrockyjaiswal
 
EdTechJoker Spring 2020 - Lecture 7 Drupal intro
EdTechJoker Spring 2020 - Lecture 7 Drupal introEdTechJoker Spring 2020 - Lecture 7 Drupal intro
EdTechJoker Spring 2020 - Lecture 7 Drupal introBryan Ollendyke
 
drush - the commandline is your friend
drush - the commandline is your frienddrush - the commandline is your friend
drush - the commandline is your friendNathan Lisgo
 
20111010 agile minds - organize your chickens - nuget for the enterprise
20111010 agile minds - organize your chickens - nuget for the enterprise20111010 agile minds - organize your chickens - nuget for the enterprise
20111010 agile minds - organize your chickens - nuget for the enterpriseXavier Decoster
 

Similar to REST Drupal (20)

Cloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloudCloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloud
 
Backbone
BackboneBackbone
Backbone
 
Headless Drupal en pratique
Headless Drupal en pratiqueHeadless Drupal en pratique
Headless Drupal en pratique
 
RESTful OSGi middleware for NoSQL databases with Docker
RESTful OSGi middleware for NoSQL databases with DockerRESTful OSGi middleware for NoSQL databases with Docker
RESTful OSGi middleware for NoSQL databases with Docker
 
Sightly_techInsight
Sightly_techInsightSightly_techInsight
Sightly_techInsight
 
Using Composer with Drupal and Drush
Using Composer with Drupal and DrushUsing Composer with Drupal and Drush
Using Composer with Drupal and Drush
 
Getting Started with DrupalGap
Getting Started with DrupalGapGetting Started with DrupalGap
Getting Started with DrupalGap
 
Building businesspost.ie using Node.js
Building businesspost.ie using Node.jsBuilding businesspost.ie using Node.js
Building businesspost.ie using Node.js
 
Parallel Query on Exadata
Parallel Query on ExadataParallel Query on Exadata
Parallel Query on Exadata
 
Aris 9 See the Future Today
Aris 9 See the Future TodayAris 9 See the Future Today
Aris 9 See the Future Today
 
Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...
Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...
Introduction to the Hadoop Ecosystem with Hadoop 2.0 aka YARN (Java Serbia Ed...
 
Custom Android Code Templates
Custom Android Code TemplatesCustom Android Code Templates
Custom Android Code Templates
 
Headless Drupal: A modern approach to (micro)services and APIs
Headless Drupal: A modern approach to (micro)services and APIsHeadless Drupal: A modern approach to (micro)services and APIs
Headless Drupal: A modern approach to (micro)services and APIs
 
Drupal
DrupalDrupal
Drupal
 
Headless Drupal
Headless DrupalHeadless Drupal
Headless Drupal
 
CouchDB and Rails on the Cloud
CouchDB and Rails on the CloudCouchDB and Rails on the Cloud
CouchDB and Rails on the Cloud
 
EdTechJoker Spring 2020 - Lecture 7 Drupal intro
EdTechJoker Spring 2020 - Lecture 7 Drupal introEdTechJoker Spring 2020 - Lecture 7 Drupal intro
EdTechJoker Spring 2020 - Lecture 7 Drupal intro
 
drush - the commandline is your friend
drush - the commandline is your frienddrush - the commandline is your friend
drush - the commandline is your friend
 
CouchDB
CouchDBCouchDB
CouchDB
 
20111010 agile minds - organize your chickens - nuget for the enterprise
20111010 agile minds - organize your chickens - nuget for the enterprise20111010 agile minds - organize your chickens - nuget for the enterprise
20111010 agile minds - organize your chickens - nuget for the enterprise
 

More from Alexandru Badiu

Behavior Driven Development with Drupal
Behavior Driven Development with DrupalBehavior Driven Development with Drupal
Behavior Driven Development with DrupalAlexandru Badiu
 
Drupal as a first class mobile platform
Drupal as a first class mobile platformDrupal as a first class mobile platform
Drupal as a first class mobile platformAlexandru Badiu
 
Cloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloudCloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloudAlexandru Badiu
 
Learning the basics of the Drupal API
Learning the basics of the Drupal APILearning the basics of the Drupal API
Learning the basics of the Drupal APIAlexandru Badiu
 
What's new in the Drupal 7 API?
What's new in the Drupal 7 API?What's new in the Drupal 7 API?
What's new in the Drupal 7 API?Alexandru Badiu
 
Drupal, Android and iPhone
Drupal, Android and iPhoneDrupal, Android and iPhone
Drupal, Android and iPhoneAlexandru Badiu
 
Concepte de programare functionala in Javascript
Concepte de programare functionala in JavascriptConcepte de programare functionala in Javascript
Concepte de programare functionala in JavascriptAlexandru Badiu
 

More from Alexandru Badiu (13)

Behavior Driven Development with Drupal
Behavior Driven Development with DrupalBehavior Driven Development with Drupal
Behavior Driven Development with Drupal
 
Drupal 8
Drupal 8Drupal 8
Drupal 8
 
Drupal as a first class mobile platform
Drupal as a first class mobile platformDrupal as a first class mobile platform
Drupal as a first class mobile platform
 
Cloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloudCloud to the rescue? How I learned to stop worrying and love the cloud
Cloud to the rescue? How I learned to stop worrying and love the cloud
 
Using Features
Using FeaturesUsing Features
Using Features
 
Learning the basics of the Drupal API
Learning the basics of the Drupal APILearning the basics of the Drupal API
Learning the basics of the Drupal API
 
What's new in the Drupal 7 API?
What's new in the Drupal 7 API?What's new in the Drupal 7 API?
What's new in the Drupal 7 API?
 
Drupal, Android and iPhone
Drupal, Android and iPhoneDrupal, Android and iPhone
Drupal, Android and iPhone
 
Publish and Subscribe
Publish and SubscribePublish and Subscribe
Publish and Subscribe
 
Using Features
Using FeaturesUsing Features
Using Features
 
Concepte de programare functionala in Javascript
Concepte de programare functionala in JavascriptConcepte de programare functionala in Javascript
Concepte de programare functionala in Javascript
 
Drupal and Solr
Drupal and SolrDrupal and Solr
Drupal and Solr
 
Prezentare Wurbe
Prezentare WurbePrezentare Wurbe
Prezentare Wurbe
 

REST Drupal

  • 1. REST Drupal Talking to Drupal from Javascript, iOS and Android Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 1 Sunday, November 18, 12
  • 2. About me I pity the fool who doesn’t use Drupal Hi, I’m Alexandru Badiu. I’m a software engineer, amateur game developer and part time Mister T impersonator. Drupal user for 9 years, board member in Drupal Romania. I work for Demotix. Twitter @voidberg Web ctrlz.ro Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 2 Sunday, November 18, 12
  • 3. REST Drupal Mobile is pretty big now days Drupal 8 Web services initiative For Drupal 7 there’s the Services module Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 3 Sunday, November 18, 12
  • 4. YASS Oh boy, yet another Services session! Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 4 Sunday, November 18, 12
  • 5. Nope Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 5 Sunday, November 18, 12
  • 6. In this session Doing “web services” without Services Doing “web services” without Drupal’s slowness Using Services ... With Javascript ... With Objective-C ... With Java Uploading files and cursing at Apple and Google Some memes Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 6 Sunday, November 18, 12
  • 7. Sorry :( Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 7 Sunday, November 18, 12
  • 8. 1 Services without services Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 8 Sunday, November 18, 12
  • 9. Why would you do that? One word: Drupal is slow Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 9 Sunday, November 18, 12
  • 10. Services without services Sometimes you really don’t need services Getting a list of news and showing some details about them. Showing nearby businesses on a map. Users submitting anonymous tips or comments. Use JSON and don’t shoot yourself in the foot by using XML or plists. Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 10 Sunday, November 18, 12
  • 11. However Services is not the slowest thing, Drupal is Shameless plug: Drupal NoBootstrap Small php library which allows you to be able to use some of Drupal's functions without bootstrapping Drupal. Used it for Solr powered instant Google like map of businesses Used it for lightning fast autocomplete Used it for coupon claiming app https://github.com/voidberg/Drupal-NoBootstrap Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 11 Sunday, November 18, 12
  • 12. 2 Services Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 12 Sunday, November 18, 12
  • 13. Services A long time ago in a Drupalcamp far, far away Last year I was complaining about Services Clunky way of connecting (session, nonce, timestamp) Inconsistent responses Bugs in json_server Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 13 Sunday, November 18, 12
  • 14. Services 3 Services 3 is much much nicer REST based No more nonce and other crap Session is kept in the HTTP calls Consistent responses Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 14 Sunday, November 18, 12
  • 15. Services 3 REST in a nutshell Resource based HTTP based CRUD GET /endpoint/resource/id POST /endpoint/resource + (post data) DELETE /endpoint/resource/id PUT /endpoint/resource/id + (post data) GET /endpoint/resource GET /services/comment?parameters[nid]=123&parameters[timestamp]=123456:123600 Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 15 Sunday, November 18, 12
  • 16. Services 3 REST in a nutshell Actions Do not target a specific resource POST /services/apachesolr/reindex Targeted actions Target a specific resource POST /services/node/123/unpublish Relationships GET /services/node/123/comments Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 16 Sunday, November 18, 12
  • 17. 3 Drupanium Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 17 Sunday, November 18, 12
  • 18. Drupanium Distribution targeted at building mobile apps For Titanium Studio Should be possible to use with Cordova Looks abandoned Simple js code to grab Decent list of core services resources http://drupanium.org/api Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 18 Sunday, November 18, 12
  • 19. 4 iOS Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 19 Sunday, November 18, 12
  • 20. Android Drupal iOS SDK The library formerly known as KBDrupalConnect Uses AFNetworking Implements all core service resources Can use OAuth Uses blocks https://github.com/workhabitinc/drupal-ios-sdk Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 20 Sunday, November 18, 12
  • 21. Drupal iOS SDK Copy AFNetworking code and Drupal iOS SDK code in project Update Settings.m with url, endpoint etc Profit! Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 21 Sunday, November 18, 12
  • 22. Drupal iOS SDK DIOSUser *user = [[DIOSUser alloc] init]; [user userLoginWithUsername:username andPassword:password success:^(AFHTTPRequestOperation *operation, id responseObject) { // Login ok DIOSSession *session = [DIOSSession sharedSession]; [session setUser:[responseObject objectForKey:@"user"]]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // Login failed [MBProgressHUD hideHUDForView:self.view animated:YES]; } ]; Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 22 Sunday, November 18, 12
  • 23. Drupal iOS SDK DIOSNode *node = [[DIOSNode alloc] init]; NSMutableDictionary *nodeData = [NSMutableDictionary new]; [nodeData setValue:[@"Node title" text] forKey:@"title"]; NSDictionary *bodyValues = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"Node title", nil] forKeys:[NSArray arrayWithObjects:@"value", nil]]; NSDictionary *languageDict = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:bodyValues] forKey:@"und"]; [nodeData setValue:languageDict forKey:@"body"]; NSDictionary *fieldValue = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"Field value", nil] forKeys:[NSArray arrayWithObjects:@"value", nil]]; NSDictionary *fieldLangDict = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:sessionValue] forKey:@"und"]; [nodeData setValue:fieldLangDict forKey:@"field_foo_bar"]; [nodeData setValue:@"nodetype" forKey:@"type"]; [nodeData setValue:@"und" forKey:@"language"]; [node nodeSave:nodeData success:^(AFHTTPRequestOperation *operation, id responseObject) { // Node was saved } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // Node save failed }]; Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 23 Sunday, November 18, 12
  • 24. Extending it @interface DemotixServices: NSObject - (void)userRegister:(NSDictionary *)user success:(void (^)(AFHTTPRequestOperation *operation, id responseObject)) success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error)) failure; @end #import "DemotixServices.h" #import "DIOSSession.h" @implementation DemotixServices #pragma mark userRegister - (void)userRegister:(NSDictionary *)user success:(void (^)(AFHTTPRequestOperation *operation, id responseObject)) success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error)) failure { [[DIOSSession sharedSession] postPath:[NSString stringWithFormat:@"%@/%@", kDiosEndpoint, @"demotix_services_user/register"] parameters:user success:success failure:failure]; } @end; Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 24 Sunday, November 18, 12
  • 25. Saving the session // After a login NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; NSArray *cookies = [cookieStorage cookies]; for (NSHTTPCookie *cookie in cookies) { if ([cookie.name hasPrefix:@"SESS"]) { [keychainS setObject:cookie.name forKey:(__bridge id)kSecAttrAccount]; [keychainS setObject:cookie.value forKey:(__bridge id)kSecValueData]; } } // On next app start NSString *sessionName = [keychainS objectForKey:(__bridge id)kSecAttrAccount]; NSString *sessionValue = [keychainS objectForKey:(__bridge id)kSecValueData]; if (![sessionName isEqualToString:@""] && ![sessionValue isEqualToString:@""]) { DIOSSession *session = [DIOSSession sharedSession]; [session setDefaultHeader:@"Cookie" value:[NSString stringWithFormat:@"%@=%@", sessionName, sessionValue]]; DIOSSystem *system = [[DIOSSystem alloc] init]; [system systemConnectwithSuccess: ^(AFHTTPRequestOperation *operation, id responseObject) { } failure:^(AFHTTPRequestOperation *operation, NSError *error) { }]; } Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 25 Sunday, November 18, 12
  • 26. File upload NSMutableURLRequest *request = [[DIOSSession sharedSession] multipartFormRequestWithMethod:@"POST" path:[NSString stringWithFormat:@"%@/%@", kDiosEndpoint, @"demotix_services_story/upload"] parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) { NSError *error; if (![formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"joined" error:&error]) { NSLog(@"An error occured: %@", error); } else { NSLog(@"No error"); } }]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { [delegate updateUploadProgress:totalBytesWritten from:totalBytesExpectedToWrite]; }]; [operation setCompletionBlockWithSuccess:success failure:failure]; [operation start]; Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 26 Sunday, November 18, 12
  • 27. File upload Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 27 Sunday, November 18, 12
  • 28. File upload AFNetworking uses the iOS API for http calls The API has a bug since iOS 3 When uploading files on 3G you get a OOM error Solution is to throttle the upload which the API has no support for AFNetworking is still working on this I ended up using ASIHttpRequest just for file upload with throttled upload on 3G Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 28 Sunday, November 18, 12
  • 29. File upload [ASIHTTPRequest setShouldThrottleBandwidthForWWAN:YES]; [ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount*throttle_amount]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%@", kDiosBaseUrl, kDiosEndpoint, @"demotix_services_story/upload"]]; __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; [request setCompletionBlock:^{ success(responseString); }]; [request setFailedBlock:^{ failure(responseString, error); }]; for (NSString * fkey in files) { NSDictionary *file = [files objectForKey:fkey]; NSString *fileName = [file valueForKey:@"name"]; [request setFile:[file valueForKey:@"fileUrl"] forKey:fileName]; } [request setShouldContinueWhenAppEntersBackground:YES]; [request setTimeOutSeconds:999]; [request setUploadProgressDelegate:self]; [request startAsynchronous]; Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 29 Sunday, November 18, 12
  • 30. 5 Android Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 30 Sunday, November 18, 12
  • 31. Android DrupalCloud is not working anymore Hasn’t been updated to Services 3 Never really liked the architecture Dandy might work, I was scared No documentation at all Source code seems over engineered Not sure it works with Services 3 (issue queue says it doesn’t) Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 31 Sunday, November 18, 12
  • 32. Android So I built my own Android Drupal SDK https://github.com/voidberg/ Android-Drupal-Sdk Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 32 Sunday, November 18, 12
  • 33. First step Let’s choose a HTTP Client It’s a mess HttpUrlConnection Been since the dark ages Had some serious bugs before Froyo Still behind the apache lib Apache HTTP Client Best option so far IMHO Has been deprecated Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 33 Sunday, November 18, 12
  • 34. We have a winner Android Asynchronous Http Client http://loopj.com/android-async-http/ You’ll be in select company - Instagram HTTP requests happen outside the UI thread Requests use a threadpool to cap concurrent resource usage Multipart file uploads with no additional third party libraries Automatic smart request retries optimized for spotty mobile connections Automatic gzip response decoding support for super-fast requests Built-in response parsing into JSON with JsonHttpResponseHandler Persistent cookie store, saves cookies into your app’s SharedPreferences Uses the Apache HTTP Client Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 34 Sunday, November 18, 12
  • 35. Using it ServicesClient client; client = new ServicesClient("http://www.example.com", "api/ mobile"); client.setBasicAuth("username","password/token"); PersistentCookieStore cookieStore; cookieStore = new PersistentCookieStore(this); client.setCookieStore(cookieStore); Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 35 Sunday, November 18, 12
  • 36. Using it SystemServices ss; ss = new SystemServices(client); JsonHttpResponseHandler connectHandler = new JsonHttpResponseHandler() { @Override public void onSuccess(JSONObject response) { JSONObject user = response.getJSONObject("user"); int uid = user.getInt("uid"); } @Override public void onFailure(Throwable e, JSONObject response) { // System.Connect call failed } @Override public void onFinish() { } }; ss.Connect(connectHandler); Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 36 Sunday, November 18, 12
  • 37. Using it UserServices us; us = new UserServices(client); JsonHttpResponseHandler loginHandler = new JsonHttpResponseHandler() { @Override public void onSuccess(JSONObject response) { boolean error = false; JSONObject user = response.getJSONObject("user"); } @Override public void onFailure(Throwable e, JSONObject response) { // Username or password were incorrect } @Override public void onFinish() { activity.hideProgressDialog(); } }; activity.showProgressDialog("Logging you in"); us.Login("username", "password"); Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 37 Sunday, November 18, 12
  • 38. Extending it public class DemotixServices { private ServicesClient client; public DemotixServices(ServicesClient c) { client = c; } public void Register(String fname, String lname, String name, String email, String password, AsyncHttpResponseHandler responseHandler) { JSONObject params = new JSONObject(); try { params.put("name", name); params.put("mail", email); params.put("pass", password); params.put("field_firstname", fname); params.put("field_lastname", lname); params.put("legal_accept", "1"); params.put("from_mobile", "1"); } catch (JSONException e) { e.printStackTrace(); } client.post("demotix_services_user/register", params, responseHandler); } } Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 38 Sunday, November 18, 12
  • 39. File upload InputStream myInputStream = blah; RequestParams params = new RequestParams(); params.put("secret_passwords", myInputStream, "passwords.txt"); File myFile = new File("/path/to/file.png"); RequestParams params = new RequestParams(); try { params.put("profile_picture", myFile); } catch(FileNotFoundException e) {} client.post(...) Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 39 Sunday, November 18, 12
  • 40. File upload Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 40 Sunday, November 18, 12
  • 41. File upload Turns out that the library tries to read all data before uploading For large files this is a problem as you’ll run out of memory immediately Solution: go low level and build the multi part yourself by attaching FileInputStreams Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 41 Sunday, November 18, 12
  • 42. File upload HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, 900 * 1000); HttpConnectionParams.setSoTimeout(httpParams, 900 * 1000); HttpClient httpclient = ServicesClient.client.getHttpClient(); HttpContext httpContext = ServicesClient.client.getHttpContext(); HttpPost httppost = new HttpPost("http://www.example.com/api/mobile/service/upload"); httppost.setParams(httpParams); MultipartEntity entity = new MultipartEntity(); // Add parameters to the post body entity.addPart("param1", new StringBody(param1.toString(),"application/json", Charset.forName("UTF-8"))); // Add file to post body InputStream istream1 = new FileInputStream("file1.jpg"); entity.addPart("file1", new InputStreamBody(istream1, "file1")); httppost.setEntity(entity); HttpResponse response = httpclient.execute(httppost, httpContext); Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 42 Sunday, November 18, 12
  • 43. File upload Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 43 Sunday, November 18, 12
  • 44. File upload Nope There is no easy way of getting the progress of an upload We fake it: use CustomMultiPartEntity instead of MultiPartEntity This class notifies a listener of the bytes read so far Calculate an approximation of the size of the post data Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 44 Sunday, November 18, 12
  • 45. File upload CustomMultiPartEntity postEntity = new CustomMultiPartEntity(new CustomMultiPartEntity.ProgressListener() { @Override public void transferred(long num) { updateUploadProgress((int) ((num / (float) totalUploadSize) * 100)); } }); try { postEntity.addPart("param1", new StringBody(param1.toString(),"application/json", Charset.forName("UTF-8"))); totalUploadSize = postEntity.getContentLength(); for (Map.Entry<String, String> entry : files.entrySet()) { try { InputStream istream = new FileInputStream(entry.getValue()); postEntity.addPart(entry.getKey(), new InputStreamBody(istream, entry.getKey())); totalUploadSize += new File(entry.getValue()).length(); } catch (FileNotFoundException e) {} } httppost.setEntity(postEntity); } catch (UnsupportedEncodingException e) {} HttpResponse response = httpclient.execute(httppost, httpContext); Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 45 Sunday, November 18, 12
  • 46. Another caveat Varnish has issues by default You have a rule that does “return post” for, well, POST calls Upload will fail with a socket exception Add another rule that does “return stream” for POST calls made to your upload url Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 46 Sunday, November 18, 12
  • 47. Thanks! Questions? Alexandru Badiu. Twitter @voidberg Web http://ctrlz.ro Email andu@ctrlz.ro D.O http://drupal.org/user/8662 Drupalcamp Arad 2012 - Alexandru Badiu - REST Drupal 47 Sunday, November 18, 12