Jump to content

Rest Helper For Processwire


clsource

Recommended Posts

Well basically when you follow the RESTful approach when creating systems, you use resources rather than actions.

you can know more here 

http://restcookbook.com/

http://restpatterns.org/

http://www.restapitutorial.com/

and this books

 https://leanpub.com/build-apis-you-wont-hate

 http://www.soa-in-practice.com/

Example if you want to make an admin for the users resource.

you can have this URL

http://api.example.com/users

In the traditional CRUD  aproach, the verb is inside the URL like

http://api.example.com/users/create

but in REST you must use only HTTP Verbs to interact,

so the same endpoint url makes different actions depending on the verb used to call it.

In our system that could be

http://api.example.com/users

GET - result in a list of users 

POST - creates a new user 

-------------------------------------

http://api.example.com/users/clsource

GET - result in the user data
PUT - updates the all the data of a specific user 

PATCH - updates a specific data field of a specific user
DELETE - deletes the user
 

Using the HTTP response codes and mime types you can notify the result of an operation, usually with a JSON or XML body for information.

The Web services Approach, specially REST web services, enables us to separate complex systems

in smaller ones, easier to mantain and test.

Basically you can have multiple backend systems that just are APIs and one frontend system that glue them all.

this add a layer of security and robustness, because you can separate your systems in different servers. A possible attack can not affect all the system, just small parts of it.

Edited by clsource
  • Like 12
  • Thanks 2
Link to comment
Share on other sites

could something like this also handled by url segments?

just read:

http://processwire.com/docs/tutorials/how-to-use-url-segments/

segments for GET, PUT, PATCH, DELETE

and the logic within the template file??

so may you have some sort of data like

www.your-pw-site.com/products/

and use url segments for getting is

www.your-pw-site.com/products/get/

data service for external pages could be done via using methods from

http://modules.processwire.com/modules/service-pages/

and put, patch, delete via API in the template?

or did i miss something.

(I'm very new to this topic - but very interested since a friend ask me to setup a bridge between a local (mostly offline) MSSQL server and a mobile App so my ideas walking with a "man in the middle" as a website app that get JSON from the local PC (=sync) and host the data for the mobile App (=sync) ----> O0 )

so sorry for loud thinking here....

Link to comment
Share on other sites

I'm also quite new to the RESTful approach but i think the whole point is that a given endpoint responds to the various http request methods, so:

www.your-pw-site.com/products/ would respond to http GET by listing the products, and http POST would add a new product.

  • Like 1
Link to comment
Share on other sites

the basic question is.

Is it possible to put the logic in some helper classes and the/a template file without changing the core?

have to try - need more time----> URGEND need for a paypal "spend more time" account... ;)

Via inside the URL would be a traditional way not REST via http response...like clsource described...

the mainpoint for my hobby project is two-way sync (one direction for now):

1. local MSSQL DB to Webserver via .net send JSON Data to a web adress (->getting the data right is my point of work - i'll try with the import Json addon and some scripts)

2. get the data from the Webserver on a mobile app via JS and JSON (like with the PageWebservice Addon from Ryan) on a next step save it in local store and so on....i've a try with the g00gle APK builder...

so until now i'm sorting what base i will use - more focus on REST (in mind of huge data and different usecases) or get it less or more simple via PHP and GET/POST?

Kind regards mr-fan

Link to comment
Share on other sites

I'm also quite new to the RESTful approach but i think the whole point is that a given endpoint responds to the various http request methods, so:

www.your-pw-site.com/products/ would respond to http GET by listing the products, and http POST would add a new product.

Yes, The verb is on the Http Request and different responses are given depending on the way you call an endpoint.

could something like this also handled by url segments?

just read:

http://processwire.com/docs/tutorials/how-to-use-url-segments/

segments for GET, PUT, PATCH, DELETE

and the logic within the template file??

so may you have some sort of data like

www.your-pw-site.com/products/

and use url segments for getting is

www.your-pw-site.com/products/get/

data service for external pages could be done via using methods from

http://modules.processwire.com/modules/service-pages/

and put, patch, delete via API in the template?

or did i miss something.

(I'm very new to this topic - but very interested since a friend ask me to setup a bridge between a local (mostly offline) MSSQL server and a mobile App so my ideas walking with a "man in the middle" as a website app that get JSON from the local PC (=sync) and host the data for the mobile App (=sync) ----> O0 )

so sorry for loud thinking here....

If you got 

www.your-pw-site.com/products/

then you can have these methods

GET -> list of products
POST -> create a new product

then if you got this particular product "computer-1"

www.your-pw-site.com/products/computer-1

you can have these methods

GET-> info of computer 1
PUT -> replace all the info of computer 1
PATCH -> replace specific info like name or sku of computer 1
DELETE -> removes computer 1

Now taking that as an example, you can have this way to program and endpoint in Processwire

1.- Create a template named "products" (with a file products.php in templates folder)

2.- Create a page that use that template, and creates  the url www.your-pw-site.com/products/ (Enable UrlSegments)

3.- Now  products.php will be coded like this

Note that Product.php is just a helper class that just have some methods for easier output formatting

<?php
require_once './includes/Rest.php';
require_once './models/Product.php';

// Vars with the default output
$code = 200;
$output = null;
$header = Rest\Header::mimeType('json');

// First check if you have Url segment1
// this segment you can have the product id

if($input->urlSegment1) {
	
	$productId = $input->urlSegment1;
	
	$product = Product::find($productId);
	
	// Check if we found a product
	if(!($product instanceOf NullPage)) {
		
		// Convert the page object to our model
		$product = new Product($product);
		
		// Detects the Verb
		if(Rest\Request::is('get')) {
			
			// json encodeable array
			$output = $product->get();
			
		} else (Rest\Request::is('put')) {
			$params = Rest\Request::params();
			$output = $product->put($params);
			
			// Could be 202 if modification is made async
			// 200 if OK
			// $code = 202;

		} else (Rest\Request::is('patch')) {
			
			$params = Rest\Request::params();
			$sku = $params['sku'];
			
			$output = $product->patch('sku', $sku);
			
			// Could be 202 if modification is made async
			// 200 if OK
			// $code = 202;

		} else (Rest\Request::is('delete')) {
			$output = $product->delete();
		}
		
		// Product not found
	} else {
		$code = 404;
		$output = Product::notFound();
	}

} else {

	// Detects the Verb
	if(Rest\Request::is('get')) {
		$params = Rest\Request::params();
		
		$page = $params['page'];
		
		$output = Product::fetch($page);
		
	} else (Rest\Request::is('post')) {
		
		$params = Rest\Request::params();
		
		// You can get the params like
		// $params['name'], 
		// $params['sku']
		// and so on
		
		$newProduct = Product::create($params);
		
		// Returns and array that can be json encoded
		$output = $newProduct->get();

		// 201 Created
		$code = 201;
	}

} // End if

// Show the response and body
http_response_code($code);
header($header);
echo json_encode($output);
  • Like 3
Link to comment
Share on other sites

  • 6 months later...

@clsource

Thank you for your REST helper. I'm using it for a project and it is working fine.

For my project I'm using basic HTTP Authetication. I added some code to the params method to fetch username and password and merge it into the $params array. The altered code is here: https://gist.github.com/gebeer/5d1447ff76e17931d944#file-rest-php-L265

For a PUT request I use $session->login() to verify username and password.

		$uId = $input->urlSegment1;

		$u = $users->get($uId);

		if ($session->login($u->name, $params["upass"])) {

			$session->logout();

                        //update user data
                }
 

In the response header of  that request I get some PW cookie data:

Set-Cookie: wire=ha6io723mkfc9v4scdib3oe8g7; path=/; HttpOnly wire=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT wire=1n8faeiva3vg7u13ijsrs24bt1; path=/; HttpOnly wire_challenge=YK0WRw0Wrd2ZAhKEUCLPOHd9iSySEPb91; expires=Tue, 07-Apr-2015 14:11:24 GMT; path=/; httponly wire=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT wire=u9m41s8b87d3ca1jp1jbl0r6k3; path=/; HttpOnly wire=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT wire=oidcbmht561qnvts2fjnq4b7p3; path=/; HttpOnly persist=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ 

 Can I avoid that somehow other than not using session? In the PW cheatsheet I couldn't find anything related to cookies.

  • Like 2
Link to comment
Share on other sites

@clsource

Thank you for your REST helper. I'm using it for a project and it is working fine.

For my project I'm using basic HTTP Authetication. I added some code to the params method to fetch username and password and merge it into the $params array. The altered code is here: https://gist.github.com/gebeer/5d1447ff76e17931d944#file-rest-php-L265

For a PUT request I use $session->login() to verify username and password.

		$uId = $input->urlSegment1;

		$u = $users->get($uId);

		if ($session->login($u->name, $params["upass"])) {

			$session->logout();

                        //update user data
                }
 

In the response header of  that request I get some PW cookie data:

Set-Cookie: wire=ha6io723mkfc9v4scdib3oe8g7; path=/; HttpOnly wire=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT wire=1n8faeiva3vg7u13ijsrs24bt1; path=/; HttpOnly wire_challenge=YK0WRw0Wrd2ZAhKEUCLPOHd9iSySEPb91; expires=Tue, 07-Apr-2015 14:11:24 GMT; path=/; httponly wire=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT wire=u9m41s8b87d3ca1jp1jbl0r6k3; path=/; HttpOnly wire=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT wire=oidcbmht561qnvts2fjnq4b7p3; path=/; HttpOnly persist=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/ 

 Can I avoid that somehow other than not using session? In the PW cheatsheet I couldn't find anything related to cookies.

Wow thanks for using this code :D

and thanks for improving it.

Well A technique I use for avoid using sessions is a token and send that token with every request.

I previously created a "token" field inside the user page. So when the user logs in a token (normally sha1) is created

and saved to his token field.

So for example you made a login request and the backend responds with this data.

{
  "status" : "ok",
  "data" : {
     "id" : 142,
     "username" : "clsource",
     "name: "Camilo",
     "token": "abc123"
  }
}

And then you need to update its name

sending a PUT request to the corresponding endpoint

PUT {

   name :  Camilo C.

}

https://api.example.com/v1/users/{token}/profile/name

in this case {token} would be "abc123"

As the token is saved in the user page you can check if the user exists quering by the token

$usr = $users->get("token=$token");

As you may notice the token gives you access for seeing and modifying the user,

so its better to keep it safe. A rule of thumb is creating a token with a 1 hour valid time.

And when the token is invalid you should request a new one in background.

Hope it helps :D

  • Like 5
Link to comment
Share on other sites

Thank you clsource!

I would like to use PW built in functionality for authenticating my users rather than implementing my own. I am aware though, that the RESTful approach is supposed to work without server side session handling. That is why I only login the user to check if the correct password was sent and then logout of the session instantly.

If there is a way to avoid the Cookie info in the response header, that would be what I'm looking for. If there is none, I will have to rethink my approach and use a token along with the password in the Authentication header. 

Also I'm a bit confused about the cookie data in the response header. wire cookies expire 1970 and there are several of them?

So if anybody can think of a PW API method to suppress cookies, that would be great.

Link to comment
Share on other sites

  • 4 weeks later...

I tried to build something similar a few days ago, but fell over this line:

json_decode(@file_get_contents('php://input')

This does work for x-www-form-urlencoded encoded data, but it fails as soon as you want to send images, which needs the data to be encoded as multipart/form-data. So effectively you can't use PUT to update images in php, especially if you need to send additional data besides the file (authentication). Just wanted to note this here. 

Alternatively one can use the way that backbone.js goes. Just use GET/POST and differ not by http method, but by something like $_POST["_method"] when working with PHP as backend.

  • Like 1
Link to comment
Share on other sites

  • 5 weeks later...

Wow!

Really awesome job there clsource.

It really should be in processwire core as RESTful services is gaining attraction lately.

What does ryan think about this?

Welcome to the forum, mosid!

Here is a discussion about RESTful API in PW where Ryan shares his thoughts about it.

And there is Ryan's module Pages Web Service.

I don't think it needs to be in the core. Thanks to PW's modular structure it is easy to implement.

Thanks @mosid and @gebeer. Yes making a module should not be difficult. Anyway sometime ago I tried to including in the core.

here's a repo with the PW fork that have Rest included. (its outdated)

https://github.com/clsource/ProcessWire/tree/dev

  • Like 1
Link to comment
Share on other sites

  • 5 months later...

It would be helpful to see some basic examples of how to use the code that clsource and gebeer have created: what a PW template would look like. Or is it already here and I'm being obtuse?

I created this helper nearly a year ago! (time passes very fast!).

In the way I learned new tricks and Processwire also have many new things that could improve the rest helper.

So that could be a really good idea.

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...