Tag Archives: REST

REST API in PHP (on Apache)

Let’s suppose you find yourself in a situation where you have to use PHP (because you’ve got an Apache server you have to work with). Coming from a tomcat and nodejs backend, what’s the closest thing one can do to make this a more familiar and easier to work with architecture?

I introduce the Slim Framework. It’s very easy to use, and if you are familiar with nodejs and express, you’ll find this familiar.

Slim suggests you install them using Composer. Being more of a programmer than IT guy, I don’t like to have to install an util to install another util. That makes my code less portable because if I move it, I’ll have to remember to tell the IT guy what dependencies to include for me. I like my code to be drop-in-ready.

Instead, I suggest you just download their latest release, unzip, and copy their Slim folder and index.php into your webapp.

I also suggest you create a folder called /rest in your webapp root. So your folder should look something like this

/htdocs
  /rest
    /index.php
    /Slim
      /Slim.php
      /Router.php
      ...

SUPER IMPORTANT STEP!!!!!
They won’t highlight this because it’s Apache-specific, but now create a .htaccess file in the /rest folder.

The contents of .htaccess should be

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Don’t worry too much about what this means. It’s basically rewriting the url when clients request the index.php file so that it can translate them to REST paths.

So that’s it!

On yeah, the code.

Here’s a sample

<?php
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();

$app = new \Slim\Slim();
$app->contentType("application/json");

$app->get('/resource/:name','getResource');

function getResource($name) {
  $resource = array(
    'name' => $name
    );
  echo json_encode($resource);
}

$app->run();
?>

Let’s go over some pieces…

require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();

This is just some required code to initialize the framework. This is specific to the download-unzip method of installing Slim. (There’s a different way of initializing if you installed using Composer.)

$app = new \Slim\Slim();
$app->contentType("application/json");

The first line creates an instance of Slim.
The next line is optional, which sets ALL responses to be of type json.
You could also include it just within each routing function to specify the content type for just those routes.

$app->run();

This starts the service..

$app->get('/resource/:name','getResource');

function getResource($name) {
  $resource = array(
    'name' => $name
    );
  echo json_encode($resource);
}

This is really the meat of the REST app. This should look very familiar to you if you’ve used node/express before.

You just write $app->get (or $app->post, $app->delete…). The first argument is the path. Notice that :name refers to a variable whose value is passed in as a parameter to the function. The following argument(s) are callables or functions. I can also give it a name of a function as I have done here.
I say the “following argument(s)” because you can pass in more than one. You can chain a series of callables, allowing you to do things like authentication, or header rewriting, …

There’s a lot that I’m not showing you but it’s fairly easy to learn once you got it up and running. So go learn it!

(I assume you know the client-side way of calling these functions, if not, browse around this blog or the internet for great tutorials on how to make REST calls using ajax, angular, or one of a million other methods)

Tagged , , ,

A (not so) simple web architecture. Part IIIb. Proxying requests

In the previous posts, I showed you how to use jsonp to bypass browser restrictions that prevent a webpage on one server (apache) from accessing resources on another (tomcat). jsonp works will but it has restrictions of its own, such as, it only allows GET. So you can’t use the full language of REST (POST, PUT, DELETE). And really, is it still REST then?

There’s a hack to get around that, but compounding hacks on top hacks is rarely a good idea unless you have a lot of old coffee grind you need to use up.

Another solution is to use the webpage’s server as a proxy server. Apache lets you do that with two modules: mod_jd and mod_proxy.

I won’t get too much into them, but here’s a short comparison I found:
mod_proxy
Pros:

  • No need for a separate module compilation and maintenance. mod_proxy, mod_proxy_http, mod_proxy_ajp and mod_proxy_balancer comes as part of standard Apache 2.2+ distribution
  • Ability to use http https or AJP protocols, even within the same balancer.

Cons:

  • mod_proxy_ajp does not support large 8K+ packet sizes.
  • Basic load balancer
  • Does not support Domain model clustering

mod_jk
Pros:

  • Advanced load balancer
  • Advanced node failure detection
  • Support for large AJP packet sizes

Cons:

  • Need to build and maintain a separate module

I decided to use mod_proxy because the module comes built into apache, which means less configuration and less possibility of error.

Here’s how simple it was to set up.
I added one line to the httpd.conf file under the main directive:

ProxyPass /pass/ http://localhost:8080/

That tells apache (served at http://localhost) that whenever a client makes a request for http://localhost/pass/ to redirect that to http://localhost:8080/.

The only slight change is the url of the call in javascript from

var matchUri = "http://localhost:8080/kodingnotes/rest/foo/read/jsonp";

to

var matchUri = "http://localhost/pass/kodingnotes/rest/foo/read";

Now, I can use the original (non-jsonp) jQuery call and apache will forward the request to the non-JXONWithPadding wrapper java method.

Tagged , , , ,

A (not so) simple web architecture. Part III. Cross-Domain Configuration

Previously, you were shown how to set up a java REST service and a javascript webpage to call the service. This assumed that the webpage resides on the same server as the REST service. If you were to move that page to another server, say an apache server to do some work on php, then you might find a strange error such as

XMLHttpRequest cannot load http://localhost:8080/kodingnotes/rest/foo/read?input=blah. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

It violates the same origin page policy. Basically, “Under the same origin policy, a web page served from server1.example.com cannot normally connect to or communicate with a server other than server1.example.com. An exception is the HTML <script> element”

Here’s your solution: jsonp

Here’s the quick cheat sheet of what you need to do.

On the Server side
Fortunately, Jersey supports jsonp. You just need to create a wrapper method that returns the json in a padded script like so:

@GET
@Produces({"application/x-javascript"})
@Path("/read/jsonp")
public JSONWithPadding getFooP(
    @QueryParam("callback") @DefaultValue("fn") String callback,
    @QueryParam("input") String input) {
	return new JSONWithPadding(new GenericEntity<Foo>(getFoo(input)) {},
								   callback);
}

On the client side
You just need to redirect your url to the jsonp version and modify the dataType to “jsonp”

var matchUri = "http://localhost:8080/kodingnotes/rest/foo/read/jsonp";

function getFoo(input) {
  $.ajax({
    type: "GET",
    dataType: "jsonp",
    url: matchUri + "?input=" + input,
    success: function(data){
       alert("Success");
    }
 });
}
Tagged , ,

A simple web architecture. Part II. The client.

And without further ado, here’s the client part.

You just need some javascript in an html file like this:

var matchUri = "http://localhost:8080/kodingnotes/rest/foo/read";

function getFoo(input) {
  $.ajax({
    type: "GET",
    dataType: "json",
    url: matchUri + "?input=" + input,
    success: function(data){
       alert("Success");
    }
 });
}
Tagged , ,

I’m not going to go over all the reasons why you should have a RESTful service with javascript front-end (e.g. clean separation of front- and back-end, reusable web service components, etc). Here I’ll just show how you can quickly set up a RESTful web service in Java and a javascript/jQuery front-end to access it.

The server part requires just 4 things:
1) Add Jersey dependency
2) Write a java bean and annotate it
3) Write a Service class
4) Deploy the servlet in web.xml

1) Add Jersey dependency

If you’re using Maven, then add the following Jersey (JAX-RS implementation) dependency to your pom.xml

<dependency>
    <groupId>com.cedarsoft.rest</groupId>
    <artifactId>jersey</artifactId>
    <version>1.0.0</version>
    <scope>compile</scope>
</dependency>


2) Write a java bean and annotate it

Create a java bean with the following:
1) annotate the class with @XmlRootElement
2) make sure there’s a null constructor (no arguments)
3) add public setters/getters

import javax.xml.bind.annotation.XmlRootElement
@XmlRootElement
public class Foo {
    private String name;
    public Foo() { }
    public String getName() { return name; }
    public void setName(String s) { name = s; }
}

3) Write a Service class

package com.kodingnotes.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path ("/foo")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class FooService {

    @GET
    @Path("/read")
    public Foo getFoo(@QueryParam("input") String input) {
        Foo foo = new Foo();
        foo.setName(input);
        return foo;
    }
}

4) Deploy the servlet in web.xml

In your web.xml, add the following servlet


<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <!-- ##### REFER TO THE PACKAGE WHERE YOU WANT JERSEY TO PICK UP THE RESOURCE CLASSES ###### -->
    <!-- ##### Use semi-colon to specify multiple packages ###### -->
    <param-value>com.kodingnotes.services</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.config.feature.logging.DisableEntitylogging</param-name>
    <param-value>false</param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>JerseyREST</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

 Edit the <param-value> of the <param-name>com.sun.jersey.config.property.packages<param-name>

A simple web architecture. Part I: The Server

Tagged , , , ,