Update Visit Entity

Entity objects should always know who they are (you know who you are, right!?) so we'll add an ID field to Visit.

To allow a Visit entity to be created before it is saved by the service, we will make the ID field optional / nullable. We'll then add an optional / default ID parameter to the ctor. This means we don't have to change our sample visit data code.

Update Visit Service

In our VisitService we'll change the SynchronizedList to a SynchronizedMap so we can easily retrieve individual Visit objects by using the ID as a key.

The save() method will check to see the given Visit entity has an ID or not. If not, it will clone the entity and set a newly generated ID at the same time.

Note we are only cloning the entity because it is const, and it is only const because it is being stored in a different thread via SynchronizedMap. If VisitService were to be backed by a database or other means, Visit would probably be non-const and we'd just set the ID on the object.

Update Routing

Now we have to tell BedSheet how to route URLs to our new ViewPage. Following the documentation for Route we can convert a segment of the request URL into a method parameter by using /** notation. So we'll update our AppModule:

Not very nice. In the ValueEncoders section we'll see an easy way to avoid this.

Tidy Up

Our web application works - but it could be better!

TypeCoercing

When passing method arguments to Route Handlers, such as our page render() methods, BedSheet is clever about it. Using TypeCoercer from BeanUtils it tries to convert the Str segments from the request URL into whatever is needed by the route handlers.

That means our render() method could just take an Int as the ID parameter:

Text render(Int id){
visit := visitService.get(id)
...
}

The conversion from Str to Int has now been taken out of our hands. So what happens now if we access a ViewPage with a non-numerical ID? Let's try it:

Wow, a 404 page! That's exactly what we want. We tried to access information about a Visit that doesn't exist. And because nothing exists for that particular URL, a 404 should be returned to the browser.

Don't worry about all that debug information being returned to your users. BedSheet only adds that when it is run in development mode. Try running BedSheet in production mode and refresh your browser.

Hint, change the -env parameter in Main.main(), or just omit it entirely. (BedSheet defaults to production mode when no env parameter is supplied.)

BsMain().main("-env prod bednap 8069".split)

ValueEncoders

Having URL segments type coerced to fit our method parameters is nice, but what if we could go further? What if BedSheet could just pass us our Visit entity?

Well, BedSheet can! We just need to tell it how! And that's the job of a ValueEncoder. A ValueEncoder converts an object to and from a Str. So we'll write one for converting Visit objects:

Because BedSheet is now handling the entire conversion of a URL Str to a Visit instance, even out of range Int IDs now return a 404 page (and not an error page).

Update Index Page

As we've been concentrating on the ViewPage we've neglected the IndexPage! The summary table needs to be updated with links to the individual detail pages. This is where it becomes handy to have the ID bundled with the entity.