Some time ago one of the rites of passage for a wannabe Perl programmer was having its own take on a templating system. I duly complied with Template::Perlish, which I’m quite happy about and I use anywhere I can. Later, it seems that many people thought they would have a take at building the next cool web service/application framework; here, I set quite low expectactions, but it’s still useful!
Table of Contents
- Table of Contents
- What is it about?
- Care to show an example?
- Should I be impressed?
- I’ve only 2 minutes left…
- Time’s up!
What is it about?
When building real stuff, quite often I have to interact with some external API, usually provided as a webservice of some sort. Not always I have direct access to it (especially from my laptop in some casual place), or want to hammer the service while doing development. The classic use case where a mock can be useful.
It’s quite easy to setup such an example application by directly using one of the available web frameworks. Among them, Mojolicious is probably best suited for the job, as it has a really low entrance fee for setting it up (a basic installation takes only two distributions and is usually very, very quick). If you already have Dancer around, anyway, it’s also very easy to setup a simple application with it too. So, this is the perfect scenario for reinventing the wheel and have a personal shot to the problem.
I’m actually cheating a lot in this, because WebService::Fake uses
Mojolicious behind the scenes. But hey, what did you expect from
something with Fake
in its name?!?
So there you go, WebService::Fake is my personal take to having something that lets you build your webservice (or web application, for what it’s worth) in some way. I’ve not prove it formally, but it should let you do almost anything, in some strange and skewed perlish way: easy things are easy, complicate things are somehow possible but you’d better do them with something different!
Care to show an example?
Sure! The web service definition is provided via a YAML file, like this:
routes:
- path: '/'
body: '{"message":"Hello, World!"}'
Suppose it’s saved as hello-world.wsf
. You can then start the faker in
a shell (assuming wsf
is in PATH
):
shell$ WEBSERVICE_FAKE=hello-world.wsf wsf daemon
[Sat Nov 26 15:30:00 2016] [info] Listening at "http://*:3000"
Server available at http://127.0.0.1:3000
The daemon
should ring some bell about Mojolicious I guess. We can
now do a GET
in another shell and see what happens:
shell$ curl -v http://localhost:3000/
* About to connect() to localhost port 3000 (#0)
* Trying ::1...
* Connection refused
* Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.26.0
> Host: localhost:3000
> Accept: */*
>
* additional stuff not fine transfer.c:1037: 0 0
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Content-Type: application/json
< Server: Mojolicious (Perl)
< Content-Length: 27
< Date: Sat, 26 Nov 2016 14:30:03 GMT
<
* Connection #0 to host localhost left intact
{"message":"Hello, World!"}* Closing connection #0
A few comments:
- it works! This can be surprising for something that prides to be fake;
- the
Server
is another confirmation we’re building on top of Mojolicious… - the default
Content-Type
is set toapplication/json
. There’s a reason why the module name starts withWebService
, after all, although you can change it as we will see shortly - whatever we set as the
body
is returned as… the body.
Should I be impressed?
Just for comparison, the equivalent Mojolicious::Lite code for the previous section would be (without too much golfing, I admit):
use Mojolicious::Lite;
get '/' => sub {shift->render(json => {message => 'Hello, World!'})};
app->start;
So yeah… WebService::Fake lets you spare a few strokes, but it’s nothing you couldn’t get directly from Mojolicious anyway.
Use the head(ers)
Let’s complicate the web service definition a bit, for example to set a new server name and set a customized header too:
routes:
- path: '/'
body: '{"message":"Hello, World!"}'
headers:
- Server: 'WebService::Fake/0.001'
X-Whatever: 'whatever is whatever'
It does what you expect:
shell$ curl -v http://localhost:3000/
#....
< HTTP/1.1 200 OK
< Content-Length: 27
< Date: Sat, 26 Nov 2016 14:46:10 GMT
< X-Whatever: whatever is whatever
< Server: WebService::Fake/0.001
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
{"message":"Hello, World!"}* Closing connection #0
This is easy with Mojolicious::Lite too of course:
use Mojolicious::Lite;
get '/' => sub {
my $c = shift;
my $headers = $c->res->headers;
$headers->server('WebService::Fake/0.001');
$headers->header('X-Whatever' => 'whatever is whatever');
$c->render(json => {message => 'Hello, World!'});
};
app->start;
It’s just slightly more complicated but still quite compact.
Mojolicious provides a few headers shortcuts but you have somehow to
remember them, although you can fallback with the generic header
method
for both known and custom ones.
The real difference is actually that Mojolicious provides a complete (and consistent) set of tools as part of the toolkit, while WebService::Fake tries to optimize for the case where you somehow already know the shape of the answer you want to get, and want to describe it as quickly as possible.
Craft your answer
As a last example for this section, suppose you also want to provide
a different return code, e.g. 203 Non-Authoritative Information
. It’s
easy to do this in our definition YAML:
routes:
- path: '/'
body: '{"message":"Hello, World!"}'
code: 203
headers:
- Server: 'WebService::Fake/0.001'
X-Whatever: 'whatever is whatever'
Again, it’s up to the promise:
shell$ curl -v http://localhost:3000/
#....
< HTTP/1.1 203 Non-Authoritative Information
< X-Whatever: whatever is whatever
< Content-Length: 27
< Server: WebService::Fake/0.001
< Date: Sat, 26 Nov 2016 14:58:20 GMT
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
{"message":"Hello, World!"}* Closing connection #0
The same thing in Mojolicious::Lite:
use Mojolicious::Lite;
get '/' => sub {
my $c = shift;
my $headers = $c->res->headers;
$headers->content_type('application/json');
$headers->server('WebService::Fake/0.001');
$headers->header('X-Whatever' => 'whatever is whatever');
$c->res->body('{"message": "Hello, World!"}');
$c->rendered(204);
};
app->start;
Things get a bit more complicated. To set the custom HTTP status code, we
have to take a different route for rendering the body (I hereby declare my
ignorance with respect to how keep the json => {...}
alive, but it’s not
essential), because render
is not useful here.
Again, it’s a matter of understanding what shortcuts are most useful in the general case: in a real application/service, Mojolicious takes sane defaults, in this whole faking someone else’s webservice context WebService::Fake goes for a different optimization route.
I’ve only 2 minutes left…
So let’s go fast. If you already have the right answers to the requests you want to fake, then you should be all set. If you need to add a bit of logic though…
Using Templates
Body and headers are actually defined in terms of Template::Perlish templates, so this does what you think:
routes:
- path: '/'
body: '{"now":"[%= scalar localtime %]"}'
Accessing variables
You can spice up your fake answers including a few elements from the request too, e.g. parameters:
routes:
- path: '/'
body: '{"name":"[% params.name %]"}'
Placeholders
Did I mention that there’s Mojolicious providing features? You can define routes with placeholders, and later access them:
routes:
- path: '/hello/:name'
body: '{"greeting": "hello", "name":"[% stash.name %]"}'
Defaults
Have a lot of routes and want to set common things once and for all?
defaults:
headers:
- Content-Type: text/plain
X-Whatever: whatever
Server: 'Something/Fake 1.0'
routes:
- path: '/hello/:name'
body: 'Hello, [% stash.name %]!'
- path: '/negate/:something'
body: '[% stash.something %] is no-no!'
Body wrapper
Suppose you have to fake some API that has some boilerplate that never changes across different routes, and you want to change only a single part. This is where a body wrapper comes handy:
defaults:
body_wrapper: |
{
"status": "OK",
"timestamp": "[%= scalar localtime %]",
"data": [% content %]
}
routes:
- path: '/hello/:name'
body: '{"greeting": "hello", "name":"[% stash.name %]"}'
- path: '/negate/:something'
body: '{"action": "negate", "what": "[% stash.something %]"}'
- path: '/text/hello/:name'
headers:
- Content-Type: text/plain
body_wrapper: ~
body: 'Hello, [% stash.name %]!'
As you can see from the last route, you can selectively disable the wrapper on a per-route basis.
Time’s up!
You have surely noticed that there’s no specific support for security and the like, there’s no explicit support for sanitizing your inputs, escaping your output, etc. etc. i.e. all those goodies that help you build something robust to put into the wild.
So what’s it good for?!? I told you in the beginning… fake web services, that’s it! It’s meant to be a sparring partner for your programs to excercise against something that can actually answer them something, not to put in front of your “customers” (so don’t try to do that!).
We didn’t go too much in depth, and you might find a few additional tricks if you’re curious enough to take a look at the docs.
It would be great to hear from you! Comment below, or abuse the code and find bugs, or file suggestions on GitHub but whatever you decide to make of it… happy faking!