Swagger is a great program for documenting your RESTful
APIs. It is made by the good folks at
Reverb. It is open source, apache
license and can be found here https://developers.helloreverb.com/swagger/
[1]
Their demo page is located at http://petstore.swagger.wordnik.com/
[2]
And the git repository for this software is located at https://github.com/wordnik/swagger-core
[3]
In this document I am going over setting up an nginx server,
installing swagger and setting up a swagger API documents in simple static json
files. Also I want to do a simple
password protection of my swagger API notes as I am currently doing this just
for internal use. (I just have never
done this in nginx so I want to document it)
This was installed on Ubuntu 12.10
Install NGINX
> sudo apt-get update
> sudo apt-get upgrade
> sudo apt-get install nginx
|
I prefer creating my own location to place my static web
pages, so I will create a default folder for my website and update the
nginx.conf file to use it.
> sudo mkdir /swagger
|
Update the /etc/nginx/nginx.conf file to use the new
directory
> sudo vi /etc/nginx/nginx.conf
|
Edit the document to the following (my server happens to be running on ip
address 192.168.0.9)
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include
/etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
keepalive_timeout 70;
server_name 192.168.0.9;
# doc
root
root /swagger;
}
}
|
Restart
the nginx server
> sudo /etc/init.d/nginx restart
|
Download swagger
You can get the swagger code by using the git tool. Ubuntu 12.10 does not have git installed by
default. Run this command to install
git.
> sudo apt-get install git
|
Use git to obtain the swagger repository
> git clone https://github.com/wordnik/swagger-ui.git
|
You need to copy the contents of the dist folder located in
the swagger-ui folder to the default web site folder of the nginx server. Here is the command to copy those files over.
> sudo cp -R swagger-ui/dist/* /swagger
|
Open your web browser and point it to your nginx
server. Mines happens to be on
192.168.0.9 so I opened http://192.168.0.9/
Success!
You can click on this example and see how it runs just
fine.
Password Protect folder
I used some information from this site http://www.howtoforge.com/basic-http-authentication-with-nginx
[4] to get some information on setting up .htpasswd with nginx. The nginx noges on auth_basic can be found
here http://wiki.nginx.org/HttpAuthBasicModule
[5]. Another site I found useful was http://www.cowboycoded.com/2011/01/12/password-protect-your-entire-site-with-nginx/
[6]
To create a password file you need the htpasswd tool which
can be obtained via the apache2-utils. Use this command to install the apache2-utils
> sudo
apt-get install apache2-utils
|
Create a .htpasswd file with your first user in it (with
their passwd). This command will create
the .htpasswd with the user "patman" adjust it to your needs.
> sudo
htpasswd -cm /swagger/.htpasswd patman
|
When you run this command it will ask you to type in your
password.
For each additional user drop the -c (which creates a new
file) . Here is an example that will add
a user to your .htpasswd file without creating a new file.
> sudo
htpasswd -m /swagger/.htpasswd patman2
|
Update nginx to handle .htpasswd
> sudo vi /etc/nginx/nginx.conf
|
Edit the document to the following
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
worker_connections
1024;
use epoll;
multi_accept on;
}
http {
include
/etc/nginx/mime.types;
default_type
application/octet-stream;
server {
auth_basic
"Restricted";
auth_basic_user_file /swagger/.htpasswd;
listen 80;
keepalive_timeout 70;
server_name 192.168.0.9;
# doc root
root /swagger;
}
}
|
This adds password protection to the entire site.
Restart
the nginx server
> sudo /etc/init.d/nginx restart
|
Success! It asked
for a password. And when I put my
username password in it opened up just fine.
Write your own swagger json files
Now I am going to update and examine the default example
files given to me by swagger. My goal
is to hand make some static swagger files that work.
Update /swagger/index.html
> cd /swagger
> sudo vi index.html
|
Here is the default index.html from the swagger example
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link
href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet'
type='text/css'/>
<link href='css/hightlight.default.css'
media='screen' rel='stylesheet' type='text/css'/>
<link href='css/screen.css'
media='screen' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js'
type='text/javascript'></script>
<script src='lib/jquery.slideto.min.js'
type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js'
type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js'
type='text/javascript'></script>
<script
src='lib/handlebars-1.0.rc.1.js' type='text/javascript'></script>
<script src='lib/underscore-min.js'
type='text/javascript'></script>
<script src='lib/backbone-min.js'
type='text/javascript'></script>
<script src='lib/swagger.js'
type='text/javascript'></script>
<script src='swagger-ui.js'
type='text/javascript'></script>
<script
src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script
type="text/javascript">
$(function () {
window.swaggerUi = new
SwaggerUi({
discoveryUrl:"http://petstore.swagger.wordnik.com/api/api-docs.json",
apiKey:"special-key",
dom_id:"swagger-ui-container",
supportHeaderParams: false,
supportedSubmitMethods:
['get', 'post', 'put'],
onComplete:
function(swaggerApi, swaggerUi){
if(console) {
console.log("Loaded SwaggerUI")
console.log(swaggerApi);
console.log(swaggerUi);
}
$('pre
code').each(function(i, e) {hljs.highlightBlock(e)});
},
onFailure: function(data) {
if(console) {
console.log("Unable to Load SwaggerUI");
console.log(data);
}
},
docExpansion:
"none"
});
window.swaggerUi.load();
});
</script>
</head>
<body>
<div id='header'>
<div class="swagger-ui-wrap">
<a id="logo"
href="http://swagger.wordnik.com">swagger</a>
<form id='api_selector'>
<div class='input
icon-btn'>
<img
id="show-pet-store-icon" src="images/pet_store_api.png"
title="Show Swagger Petstore Example Apis">
</div>
<div class='input
icon-btn'>
<img
id="show-wordnik-dev-icon" src="images/wordnik_api.png"
title="Show Wordnik Developer Apis">
</div>
<div
class='input'><input placeholder="http://example.com/api"
id="input_baseUrl" name="baseUrl"
type="text"/></div>
<div
class='input'><input placeholder="api_key"
id="input_apiKey" name="apiKey"
type="text"/></div>
<div class='input'><a
id="explore" href="#">Explore</a></div>
</form>
</div>
</div>
<div
id="message-bar" class="swagger-ui-wrap">
</div>
<div
id="swagger-ui-container" class="swagger-ui-wrap">
</div>
</body>
</html>
|
I am going to make a bare bones index.html here is my simpler version
<!DOCTYPE html>
<html>
<head>
<title>Swagger UI</title>
<link
href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet'
type='text/css'/>
<link
href='css/hightlight.default.css' media='screen' rel='stylesheet'
type='text/css'/>
<link href='css/screen.css'
media='screen' rel='stylesheet' type='text/css'/>
<script src='lib/jquery-1.8.0.min.js'
type='text/javascript'></script>
<script
src='lib/jquery.slideto.min.js' type='text/javascript'></script>
<script src='lib/jquery.wiggle.min.js'
type='text/javascript'></script>
<script src='lib/jquery.ba-bbq.min.js'
type='text/javascript'></script>
<script src='lib/handlebars-1.0.rc.1.js'
type='text/javascript'></script>
<script src='lib/underscore-min.js'
type='text/javascript'></script>
<script src='lib/backbone-min.js'
type='text/javascript'></script>
<script src='lib/swagger.js'
type='text/javascript'></script>
<script src='swagger-ui.js'
type='text/javascript'></script>
<script
src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
<script
type="text/javascript">
$(function () {
window.swaggerUi = new
SwaggerUi({
discoveryUrl:"http://petstore.swagger.wordnik.com/api/api-docs.json",
dom_id:"swagger-ui-container",
supportedSubmitMethods:
['get', 'post', 'put'],
});
window.swaggerUi.load();
});
</script>
</head>
<body>
<br/>
<div
id="swagger-ui-container" class="swagger-ui-wrap">
</body>
</html>
|
If you open the site now you should see this
The header is now gone that allows you to load other
swagger apis (if you submit the correct web address of your swagger json files
to it)
And the /store api is now missing.
It is only missing because I removed this line from the
javascript that created the SwaggerUI object
If you put it back in and reopen the site you will see the
/store again
For now I am removing the apiKey because I want to start
on the very basic level and continue building from there.
Update the index.html again.
Change the
discoveryURL to a local json file
Change
To
To reference a file at
http://192.168.0.9/api/api-docs.json
Create the api-docs.json file
> sudo mkdir /swagger/api
> cd /swagger/api
> sudo vi api-docs.json
|
Here is swaggers page on their resource listings https://github.com/wordnik/swagger-core/wiki/Resource-Listing
[7]
Here is the json file I came up with that only defines one
api that is a simple get resource.
{
"basePath":
"http://192.168.0.9/",
"resourcePath": "/",
"swaggerVersion":
"1.1",
"apiVersion": "0.1",
"apis": [
{
"path":
"/jsonfiles/test",
"format":
"json",
"description":
"Download a json file",
"operations": [
{
"httpMethod":
"GET",
"summary":
"Downloads a json file",
"responseClass": "void",
"nickname": "jsonTest"
}
]
}
]
}
|
Here is a description of what is going on line by
line (as best as I can
explain/understand it at this stage of learning swagger)
"basePath": "http://192.168.0.9/",
"resourcePath": "/",
"swaggerVersion":
"1.1",
"apiVersion": "0.1",
|
basePath
The url
location of your web based api. (in my
test case its 192.168.0.9 in a real case
it would be your web site url to the base of your API)
resourcePath
The
relative path to the API from the basePath
swaggerVersion
The swagger
version you are using (currently 1.0 and
1.1 are valid)
apiVersion
The version
number of the api being displayed (your
api version number… I guess it does not have to be a number)
"apis": [
{
"path":
"/jsonfiles/test",
"format":
"json",
"description":
"Download a json file",
|
Here is the meat where you define your apis. Swaggers api declaration page can be found
here https://github.com/wordnik/swagger-core/wiki/API-Declaration
[8]
apis
A list of
APIS (urls) that you want listed
path
The
relative path (from the resourcePath) to the resource you are defining
description
The
description of the resource (I do not see where this is used on the display
page)
"operations": [
{
"httpMethod":
"GET",
"summary":
"Downloads a json file",
"responseClass": "void",
"nickname":
"jsonTest"
}
]
|
operations
A list of
operations allowed on this resource (GET, POST, PUT, and DELETE)
httpMethod
This can be
set to GET, POST, PUT, and DELETE
summary
This
description is shown next to the resource on the web page
responseClass
This is
what is returned from the method it can be void, primitive, complex or
container. For this simple case I made
it void
nickname
this is
used by the code generator
Test it out
Reload the web page and you should see a simple slash
Click on the / and you will see all the resources that you
defined (in this case just the 1)
Now click on the "Try it Out!" button. This button will issue a GET to the defined
resource. (this is the really cool thing
about swagger aside from being a great documentation tool it's also a good
place to test
But the results in this case is a 404 error with a generated
error page from the nginx server. This
is because I did not put anything at http://192.168.0.9/jsonfiles/test
So let me remedy that right now
> sudo mkdir /swagger/jsonfiles
> sudo vi /swagger/jsonfiles/test
|
I put the following simple json file into it
{
"names": [
{
"firstName":
"John",
"lastName":
"Smith"
},
{
"firstName":
"Fred",
"lastName":
"black"
},
{
"firstName":
"jeff",
"lastName":
"fischer"
}
]
}
|
Reload
the page again
Now
it GETs the json file defined in the api
It
also shows the response code and the response headers!
This
could really replace using curl in a lot of simple quick tests that need to be
repeated a few times.
The
Content-Type for this json file is being served up as an
application/octet-stream from my nginx server.
I would like to update the swagger api declaration to GET it as a
application/json Content-Type. I found
this post talking about adding a feature like this in version 1.2 of swagger https://github.com/wordnik/swagger-ui/issues/119
[9]
OK,
there is a way to do this, it took me some time but I figured it out.
Give
me some grace as I may be long winded in explaining this.
First
you need to update you api-docs.json file and change the responseClass in the
operation to "String". After
you do this you need to add "produces" and specify the content types
this resource can be obtained as.
Here
is my updated api-docs.json
{
"basePath":
"http://192.168.0.9/",
"resourcePath": "/",
"swaggerVersion":
"1.2",
"apiVersion": "0.1",
"apis": [
{
"path":
"/jsonfiles/test",
"format":
"json",
"description":
"TEST Download a json file",
"operations": [
{
"httpMethod":
"GET",
"summary":
"Downloads a json file",
"responseClass": "String",
"nickname":
"jsonTest",
"produces": [
"application/json",
"text/html"
]
}
]
}
]
}
|
I set this to accept application.json and text/html
After this is updated reload your page
You
should see that you now have a pull down showing which Response Content Types
you can get. Select one and click Try it
Out!
It
does load, but it gets the wrong Content-Type.
I
could see that when I tried to get application/json it did try to get that.
I
want to make this a bit better test, while still being a static test. I am going to update nginx to serve all
files from the /jsonfile folder as application/json
Updating nginx to serve application/json
Open
up the nginx conf file
> sudo vi /etc/nginx/nginx.conf
|
Edit the document to the following (my server happens to be running on ip
address 192.168.0.9)
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
worker_connections
1024;
use epoll;
multi_accept on;
}
http {
include
/etc/nginx/mime.types;
default_type
application/octet-stream;
server {
auth_basic
"Restricted";
auth_basic_user_file /swagger/.htpasswd;
listen 80;
keepalive_timeout 70;
server_name 192.168.0.9;
# doc root
root /swagger;
location /jsonfiles {
types
{}
default_type application/json;
}
}
}
|
I
just added
location
/jsonfiles {
types {}
default_type
application/json;
}
|
Which
says the default type for all files in this folder will be set to
application/json
Restart
the nginx server
> sudo /etc/init.d/nginx restart
|
WARNING: when I
reloaded the page again it cached the old page so it looked like it was not
working. I had to clear the cache to see
the results I expected..
Now when I try it again I get the application/json I was
expecting
If I change and try to get text/html it seems to just be using the cache and I get
the same results… When I started a new
browser and emptied the cache I got the same result when asking for text/html
which makes sense as I set up the folder to do just that in nginx.
I think I will call this a success at this point! There is
a lot more to go over but I wanted to get the bare basics working. I am sure I will have more write ups on
Swagger going over POST, PUT, DELETE, using parameters having your code auto
generate a swagger api document, etc. but for today I think this is enough.
References
[1] Swagger main page
Visited 6/2013
[2] Swagger Demo Page
Visited 6/2013
[3] Swagger github
repository
Visited 6/2013
[4] Basic HTTP
Authentication With Nginx
Visited 6/2013
[5] HttpAuthBasicModule
Visited 6/2013
[6] Password protect
your entire site with Nginx
Visited 6/2013
[7] Swagger resource
listing
Visited 6/2013
[8] Swagger: API
Declaration
Visited 6/2013
[9] How can multiple response
content types and parameter content type be set?
Visited 6/2013
[10] Fiddler main website
Visited 6/2013
Very helpful. Exactly what I was looking for.
ReplyDeletePerfect ! If you know how to install swagger editor on swagger, can you also add it please ?
ReplyDeleteI agree with Jerry very helpful, thank you very much!
ReplyDeleteit is not understand...
Deletereferrence link are not work
ReplyDeletei am not understand. please improve your answer
ReplyDelete