Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

General Questions /

General questions about getting started with SilverStripe that don't fit in any of the categories above.

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

Exposing the Member object to the RESTful API


Go to End


2 Posts   3083 Views

Avatar
adrian

Community Member, 5 Posts

2 May 2011 at 3:54pm

Edited: 03/05/2011 12:06am

Hi there,

I'm trying to expose some aspects of my Member object to a 3rd party through the RESTful Server API. The 3rd party needs to be able to create members and update existing members with some data in an additional column called "CompetitionEntryURL".

I've created the additional column in the Member object by using the extraStatics method of a class that extends the DataObjectDecorator like so:

class MemberDecorator extends DataObjectDecorator {

    //Add extra database fields
    public function extraStatics()
    {   
        return array(
            'db' => array(
	            'CompetitionEntryURL'=>'Text'
            )
        );
    }
}

That bit works great.

Then I'd like to expose the Member object via the RESTful Server API and it is at this point I strike some problems:

  • - Rather than extend the Member class, I tried simply exposing the MemberDecorator by also setting $api_access = true in the extraStatics function. That however doesn't provide access to the actual member objects through REST, my instincts were that the DataObjectDecorator mechanism didn't seem right for this anyway. UPDATE: This did actually work, see my next post

  • - I then created a subclass of Member called MemberEx and enabled it through REST by setting the $api_access static with the appropriate permissions to ensure I'm only exposing columns and functionality that are appropriate. This works great adding Members via REST. The problem of course then is that all existing members in the database aren't of type "MemberEx" so their new CompetitionEntryURL column can't be edited through REST. This is despite the fact that CompetitionEntryURL is a column of the Member object (via the aforementioned MemberDecorator). Any new members added through here are of class type "MemberEx" and so their CompetitionEntryURL can be updated no problem. The other problem I have is that when I attempt to add a member who already exists through the exposed MemberEx class it simply returns a HTTP 500 header because it fails validation, fair enough, but I'd love to be able to create a custom method via REST that can be passed an email address so I can return an actual memberid if the member already exists.

  • - Ideally I'd just enable $api_access on the inbuilt member object itself, with the required permissions of course, but that doesn't seem possible (nor does it seem appropriate). In any case $api_access is a static so I tried setting Member::api_access = true; in _config.php but that doesn't work.

Really all that serves as background. I think my issues could be resolved by simply having the ability to call a custom method via the RESTful Server API.

e.g. http://www.servername.com/api/v1/MemberEx/<METHOD_NAME>;

However this doesn't seem possible, I can call a simple method in an instance of the MemberEx class by
http://www.servername.com/api/v1/MemberEx/<ID>/<METHOD_NAME>;

Note the addition of <ID>, which is really the problem because what I'm after is more like a static method of the MemberEx class that can be called over REST without being tied to an instance of the class.

I also can't see how you would POST parameters to a custom method.

Of course I could use the SOAPModelAccess to define a SOAP method, but the documentation on that isn't great and I simply can't get it to work.

BTW. Thanks to Willr for assisting me on this, helped me get part the way there with not so much information from me.

Regards,
Adrian

Avatar
adrian

Community Member, 5 Posts

3 May 2011 at 12:00am

Update on this, I'm pretty much all sorted out now. The DataObjectDecorator is the way to go if you want to expose the Member object via the RESTful Server API. Absolutely no need to subclass the member object which doesn't really work as you'd hope anyway. The key is to ensure the $api_access value is set inside the extraStatics() method. Also important to use it in such a way that you restrain what columns can be queried rather than just setting it to true. e.g.:

class MemberDecorator extends DataObjectDecorator {

    //Add extra database fields
    public function extraStatics()
    {   
        return array(
           // This is the extra column I added
           // which is the usual thing I use a decorator for
            'db' => array(
	            'CompetitionEntryURL'=>'Varchar(1024)'
            ),

	    // This is the additional piece to expose the member object
            // I didn't know you could do this through the decorator
            'api_access' => array(
                                // Note the constraint of the columns rather than just returning
                                // a blanket true which would expose everything.
				'view' => array('ID'),
                                // Note too the extra column added through the decorator can be
                                // included here just like a normal member object column.
				'edit' => array('FirstName', 'Surname', 'Email', 'CompetitionEntryURL')
			)
        );
        
    }

        // These can contain your application specific logic to ensure you are 
        // restricting access appropriately. e.g. by requiring authentication or 
        // only allowing certain IP addresses etc...
 	function canEdit() { return true; } 
   	function canView() { return true; } 
   	function canCreate() { return true; }
 }

Creating a New Member

So having setup the Member DataObjectDecorator, to create a new member record via the REST API:

POST to http://www.mydomain.com/api/v1/Member

And your payload should contain something like:
<Member>
<FirstName>Test</FirstName>
<Surname>Testing</Surname>
<Email>testing@testing.com</Email>
</Member>

(You can even send a password in here and it will go through all the proper security layers in Silverstripe to sha encrypt it before it reaches the database, but please do it over SSL and with an authenticated REST call).

If successful you'll get back a HTTP 201 response with a view of the record with the columns filtered to only include those which you specified in the $api_access static. e.g.:

<?xml version="1.0" encoding="UTF-8"?>
<Member href="http://www.mydomain.com/api/v1/Member/123.xml">
<ID>123</ID>
</Member>

Where 123 is the MemberID of the newly created member.

You'll get returned a HTTP 500 if the member's email address already exists or if there's some other validation error and the body of the response will just be your site's usually 500 page. I don't know how to customise this and make it a proper XML response.

Editing a Member

PUT http://www.mydomain.com/api/v1/Member/<ID>;

Where <ID> is the memberid e.g. http://www.mydomain.com/api/v1/Member/123

So for example to update the newly created member record just perform a HTTP PUT to that URL with a new XML packet such as:
<Member>
<CompetitionEntryURL>http://www.somedomain.com/myentry.jpg</CompetitionEntryURL>;
</Member>

Notice it works fine with the new column that was added through the member decorator.

If successful it will return an HTTP 301 with the following response:
<?xml version="1.0" encoding="UTF-8"?>
<Member href="http://www.mydomain.com/api/v1/Member/123.xml">
<ID>123</ID>
</Member>

Basically the same response as when you did the add, except for the HTTP 301 instead of a 201. If you attempt to update an invalid MemberID you'll get back a HTTP 404.

Querying the Member Database

GET http://www.mydomain.com/api/v1/Member?Email=<EMAIL>;

e.g. http://www.mydomain.com/api/v1/Member?Email=testing@testing.com

You'll always get back a HTTP 200 header, but if the email address already exists the response will look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<DataObjectSet totalSize="1">
<Member href="http://www.mydomain.com/api/v1/Member/123.xml">
<ID>123</ID>
</Member></DataObjectSet>

Where 123 is the MemberID. Again, you only get returned the columns you specified in the $api_access static.

If the email address did not exist the response will look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<DataObjectSet totalSize="0">
</DataObjectSet>

That's it! I hope that helps people who are looking to perform some operations on the member object via REST in a secure manner that is also using SilverStripe's features in the way they were intended (I think!?).

Regards,
Adrian