Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Column name

Type

Constraints

id

integer

primary key

title

string(255)

 

content

string(4000)

 

lastModified

timestamp

 

creationDate

timestamp

 

author

integer

relation with Author

...

Column name

Type

Constraints

id

integer

primary key

firstName

string(50)

 

lastName

string(50)

 

email

string(100)

unique passwd

string(16)

 

Creating the EOModel

To create the database, we will first create a EOModel and use migrations to build the database on the file system (H2 will take care of creating the database file).

...

Attribute name

Column

Prototype

content

content

longtext lastModified

lastModified

dateTime

creationDate

creationDate

dateTime

...

Attribute name

Column

Prototype

firstName

firstName

varchar50

lastName

lastName

varchar50

email

email

varchar100

passwd

passwd

varchar16

Final list of attributes should look like this:

...

Code Block
public String fullName() {
	  return this.firstName() + " " + this.lastName();
	}

Nothing fancy here. Now open BlogEntry.java and add the following method:

Code Block

	@Override
	public void awakeFromInsertion(EOEditingContext editingContext) {
	  super.awakeFromInsertion(editingContext);
	  this.setCreationDate(new NSTimestamp());
	}

Why are we adding this? awakeFromInsertion is a very good way of setting default values when creating a new instance of a Enterprise Object (EO). In this case, we want to set automatically the creation date without having the user to add that value.

Now, let's use migrations to actually create the database.

...

Code Block
BlogRest[62990] INFO  er.extensions.migration.ERXMigrator  - Upgrading BlogModel to version 0 with migration 'your.app.model.migrations.BlogModel0@4743bf3d'
BlogRest[62990] INFO  er.extensions.jdbc.ERXJDBCUtilities  - Executing CREATE TABLE Author(email VARCHAR(100) NOT NULL, firstName VARCHAR(50) NOT NULL, id INTEGER NOT NULL, lastName VARCHAR(50) NOT NULL, passwd VARCHAR(16) NOT NULL)
BlogRest[62990] INFO  er.extensions.jdbc.ERXJDBCUtilities  - Executing ALTER TABLE Author ADD PRIMARY KEY (id)
BlogRest[62990] INFO  er.extensions.jdbc.ERXJDBCUtilities  - Executing CREATE TABLE BlogEntry(authorID INTEGER NOT NULL, content TIMESTAMP NOT NULL, creationDate TIMESTAMP NOT NULL, id INTEGER NOT NULL, lastModified TIMESTAMP NOT NULL, title VARCHAR(255) NOT NULL)
BlogRest[62990] INFO  er.extensions.jdbc.ERXJDBCUtilities  - Executing ALTER TABLE BlogEntry ADD PRIMARY KEY (id)
BlogRest[62990] INFO  er.extensions.jdbc.ERXJDBCUtilities  - Executing ALTER TABLE BlogEntry ADD CONSTRAINT "FOREIGN_KEY_BLOGENTRY_AUTHORID_AUTHOR_ID" FOREIGN KEY (authorID) REFERENCES Author (id)
BlogRest[62990] DEBUG NSLog  -  evaluateExpression: <er.h2.jdbcadaptor.ERH2PlugIn$H2Expression: "UPDATE _dbupdater SET version = ? WHERE modelname = ?" withBindings: 1:0(version), 2:"BlogModel"(modelName)>

...

ERRest needs controllers to act as a broker between working with the objects and the routes. So let's create a controller for BlogEntry.

Create a Java class named BlogEntryController, in the your.app.rest.controllers package, that will extend from er.rest.routes.ERXDefaultRouteController. Click Finish.

When you extend from ERXDefaultRouteController, a bunch of methods are added to the subclass. Let's see what they are for.

  • updateAction: to update a specific instance of BlogEntry
  • destroyAction: to delete a specific instance of BlogEntry
  • showAction: to get one specific instance of BlogEntry
  • createAction: to create a new object (a new instance of BlogEntry)
  • indexAction: to list all (or a sublist) of the objects.
Info

In Project Wonder, Action at the end of a method is a convention for REST and Direct Actions, when you call those methods from certain components, you don't need to add the Action part.

For this tutorial, we will implement the createAction and showAction methods. But first, we need to create a key filter. A key filter will... filter the input and the output of REST request so that you don't have to send all attributes for a blog entry. For example, we want to show the details for an author, but we don't want to show the password for the author (in real-life, the password would be encrypted)!

Add this method in BlogEntryController:

Code Block

  protected ERXKeyFilter filter() {
    ERXKeyFilter personFilter = ERXKeyFilter.filterWithAttributes();
    personFilter.setAnonymousUpdateEnabled(true);

    ERXKeyFilter filter = ERXKeyFilter.filterWithAttributes();
    filter.include(BlogEntry.AUTHOR, personFilter);
    filter.setUnknownKeyIgnored(true);

    return filter;
  }

Now, let's implement the creationAction method:

Code Block

  public WOActionResults createAction() throws Throwable {
    BlogEntry entry = create(filter());
    editingContext().saveChanges();
    return response(entry, filter());
  }

In 3 lines of code, you can create an object based on the request, save the new object to the database and return the new object in the response. Not bad, eh?

Last step in the controller: implementing the showAction method. Again, the code is simple:

Code Block

  public WOActionResults indexAction() throws Throwable {
    NSArray<BlogEntry> entries = BlogEntry.fetchAllBlogEntries(editingContext());
    return response(entries, filter());
  }

That code simply fetch all blog entries and return them in the response.

We can now go to the next step: adding the routes.

Adding the routes

A route in ERRest is simply a way to define the URL for the entities and to specify which controller the route should use. When your controller extends from ERXDefaultRouteController, it's easy to register a controller and a route. In Application.java, in the Application constructor, add the following code:

Code Block

    ERXRouteRequestHandler restRequestHandler = new ERXRouteRequestHandler();
    restRequestHandler.addDefaultRoutes(BlogEntry.ENTITY_NAME);
    ERXRouteRequestHandler.register(restRequestHandler);
    setDefaultRequestHandler(restRequestHandler);

The addDefaultRoutes method do all of the required magic, and use convention. That's why we had to name the controller BlogEntryController, because the convention is <EntityName>Controller.

We are now reading to add and list blog postings! Start the application and take notice of the URL. It should be something like _http://yourip:someport/cgi-bin/WebObjects/BlogRest.woa_Image Added

Adding posts and authors with curl

Since we didn't implement any HTML for our REST routes, we will create blog entries with curl, an open source HTTP client that is bundled with Mac OS X (you can use another client, like wget, if you like too). So let's create a blog entry.

To create a blog entry, you need to use the POST HTTP method. We will use JSON as the format since it's a bit less chatty than XML. So if the URL to the application is http://192.168.0.102:52406/cgi-bin/WebObjects/BlogRest.woa_Image Added, the full _curl command will be:

Code Block
curl -X POST -v -d '{ "title": "First post", "content": "Some text", "author": { "firstName": "Pascal", "lastName": "Robert", "email": "probert@macti.ca"  } }' http://192.168.0.102:52406/cgi-bin/WebObjects/BlogRest.woa/ra/blogEntries.json

The response should look this:

Code Block

HTTP/1.0 201 Apple WebObjects
Content-Length: 249
x-webobjects-loadaverage: 0
Content-Type: application/json

{"id":1,"type":"BlogEntry","content":"Some text","creationDate":"2011-12-27T21:59:08Z","title":"First post","author":{"id":1,"type":"Author","email":"probert@macti.ca","firstName":"Pascal","lastName":"Robert"}}

To get a list of blog entries:

Code Block

curl -X GET http://192.168.0.102:52406/cgi-bin/WebObjects/BlogRest.woa/ra/blogEntries.json

Adding HTML views for blog posts