HTTPD Library
The HTTPD library can be used to create a mini web-server. You may use this for things such as
controlling your script via a web browser. At this time, SSL is not available, so all content
served over the HTTPD library will be unsecure; please keep this in mind when designing your project.
#Loading the HTTPD Library
The HTTPD library is optional. In order to use this library, you must require it in one
of your source files (preferably main.lua) prior to use. It only needs to be required once in a
single file. It is recommended you do this at the top of your main.lua file,
outside of any functions or other code, and you may do this by using this code:
Example:
require 'httpd/httpd';
#Supported Methods
#Constructor
httpd Httpd()
httpd Httpd(table config)
Creates a new HTTPD server object. If 'config' is not given, defaults are assumed. By passing
in a config, you may control the IP to listen to, port to listen on, and which directories to
look in for supporting objects (controllers, views, assets, etc.).
As soon as the object is created, it will begin to listen for any web traffic, however, you must
also be sure to pass event data back to your HTTPD instance. See Httpd:handleEvent()
for more information.
Example with defaults
require 'httpd/httpd';
httpd = Httpd();
Example with custom config
httpd = Httpd({
ip = '0.0.0.0', -- The IP to bind to; use 0.0.0.0 to accept any connection; default 127.0.0.1
port = 80, -- The port to bind to; default 8080
controllerDir = './Controllers/', -- Folder to look for controller objects; default /Controllers/
viewDir = './Views/', -- Folder to look for view files & templates; default /Views/
staticDir = './Static/', -- Folder to look for static files (images, css, javascript, etc.); default /Static/
)};
#Httpd:handleEvent
boolean Httpd:handleEvent(string eventType, ...)
Passes an event off to be handled by the Httpd. Returns true if the
event was handled by this Httpd, otherwise it returns false. You should
place this in your macro.event() function and check its return result.
If you do not use this function to pass off event data to a created Httpd, then it will not be able
to function properly as all incoming socket data is handled this way. This will cause your
web server to be unresponsive, so remember to call it!.
Example:
function macro.event(e, ...)
if( not httpd:handleEvent(e, ...) ) then
-- Wasn't handled by the HTTPD, so we might need to handle it some other way
end
end
#Routes
When initiated, the Httpd expects to be able to load a routes.lua file which is located in the
same directory as your calling script(probably main.lua). This routes file should contain
calls to the Route object; this object is instantiated for you automatically when you load the HTTPD library.
In this file, you will tell the Route object to mount various Controllers to URIs
via the Route:controller() function.
#Route:controller
Route:controller(string uriSegment, string controllerName)
This function maps the first URI segment of a URL to a specific controller, which must be the
exact name of the controller class. For example:
Example:
Route:controller('/', 'HomeController'); -- This is a special URI; this is your base page, as if you accessed http://127.0.0.1:8080 (assuming default config)
Route:controller('home', 'HomeController'); -- Just like above, but the home controller is now accessible via / OR /home
Route:controller('test', 'TestController'); -- Just some other controller for an example
#Controllers
Controllers are where the processing for your routes happens; they handle the request the user provided and
will be responsible for returning the result. Methods for each accepted URI should be prefixed with either
"get" or "post" depending on in the request is
a GET or POST method. For example, you are likely to have a getIndex() method on your HomeController that
will render your site's homepage.
A Request object will be passed to the routed method; this can be used to
handle any GET/POST parameters.
A Controller should also return the rendered result. You may directly return the HTML as a
Response but it is recommended to render a view with
View:make().
Controllers/HomeController.lua
HomeController = class.new();
-- This handles any request to get the page at http://127.0.0.1:8080
function HomeController:getIndex(request)
local dashboardData = getDashboardData(); -- Pretend getDashboardData() grabs some data we want to display
return View:make('home', dashboardData); -- Render the page and return it to the user
end
-- If we instead tried to POST data to http://127.0.0.1:8080, it gets routed here instead
function HomeController:postIndex(request)
local searchString = request:input('search');
local searchResults = getSearchResults(searchString); -- Pretend this does some actual searching
return View:make('search', searchResults);
end
#Requests
#Request:input
number|string Request:input(string inputName)
Returns an input from either POST-ed variables or GET variables with a field that matches the
'inputName'. For example, http://127.0.0.1:8080?searchString=blah is a GET request that will have
a Request with 'searchString' = "blah." The type returned will be either a number
or a string depending on the input that was received.
If you are expecting to receive a file, use Request:file() instead.
Example:
-- For accepting a request over GET method
function SearchController:getSearch(request)
local searchString = request:input('searchString');
end
-- For accepting a request over POST method
function SearchController:postSearch(request)
local searchString = request:input('searchString');
end
#Request:file
number|string Request:file(string inputName)
Like Request:input(), except it will return a file. The file will
be returned as a table with fields for name, filename, size, and file content.
Example:
function UploadController:postFile(request)
local file = request:file('uploadFile'); -- Or whatever we named our file upload field
local name = file.name;
local fileName = file.fileName;
local fileSize = file.size; -- Size in bytes
local fileContents = file.content; -- Contents of the file uploaded, as a string
end
#Request:oldInput
number|string Request:oldInput(string inputName,
number|string default)
Like Request:input(), except geared towards "old" input after a validation
failure. This is useful for re-populating a form in your View after submission.
If the given field has no input, then it will return 'default' (which defaults to '') instead.
Views/search.view.lua
<input type="text" name="searchString" value="{{ request:oldInput('searchString') }}" placeholder="Search for..." />
#Request:all
table Request:all()
Returns all inputs (GET and POST) as a table.
#Request:getCookie
number|string Request:getCookie(string name)
Returns the value of a set cookie specified by 'name'.
Example:
function UserControlPanelController:getIndex(request)
local loginCookie = request:getCookie('login'); -- We might store our login as a cookie and need to validate it here
end
#Request:uriSegment
string Request:uriSegment(number index, string default)
Returns the URI segment at a given index, or the specified default if it does not exist.
A URI segment refers to strings, separated by a '/', in the URI section of a URL. For example,
http://127.0.0.1:8080/home/index/segment3/segment4/segmentn?notasegment=1 represents a full URL
where the segment at index 1 (which typically represents the controller) is 'home', index 2
(which typically represents the method/action) is 'index', index 3 is 'segment3', and so on.
Example:
-- http://127.0.0.1:8080/home/index/whatever would be routed here
function HomeController:getIndex(request)
local something = request:uriSegment(3); -- 'something' should now be assigned 'whatever'
end
#Request:getErrors
string Request:getErrors()
Returns all validation errors, as a string, separated by "<br />\n"
In any view which may have validation errors
@if( request:getErrors() )
<h1>Errors:</h1>
{{ request:getErrors() }}
@endif
#Views
The HTTPD library makes use ov Views to separate logic (which should go into controllers) from
page displays. Views use a Blade-like templating engine. Your Views will be HTML snippets (with some
extra syntax and control structures), that end with the ".view.lua" extension.
#Templates
A template is a special View that other views may choose to extend upon using the
@extend directive. A very simplistic template might look something like this:
Example:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
@@section('content')
</body>
</html>
Once you have a template (which is optional, but highly recommended), any other View can extend
it by injecting content into its sections.
Example:
@extends('template')
@beginsection('content')
Hello World!
@endsection
#View:make
Response View:make(string viewName)
Response View:make(string viewName, table data)
Fully takes care of rendering a view for you and returns the result as a Response.
You should specify the view to render ('viewName') without the "view.lua" extension. You may
optionally pass additional variables that will be available to the view as a table of key/value pairs.
Controllers/TestController.lua
function TestController:getIndex(request)
local data = {
name = Bob,
age = 8,
};
return View:make('test', data);
end
Views/test.view.lua
<html>
<body>
<h1>Test</h1>
Hello, my name is {{ name }} and I am {{ age }} years old.
</body>
</html>
#@extends()
@extends(string viewName)
"Extend" a view by using it as a template. Any sections in this view
will be inserted into the proper place in the template.
See Templates for more information.
#@section()
@section(string sectionname)
Creates a placeholder (in a template) that can be overriden by another view.
See Templates and @beginsection
for more info.
#@beginsection()
@beginsection(string sectionname)
Creates a section that can be placed into the template; if it has a @section()
with a matching name, this section will override its content.
The section will continue until it reaches a paired @endsection directive.
Example:
@beginsection('mySection')
Everything in here will go into the 'mySection' section.
@endsection
#@include()
@include(string viewName)
The @include directive can be used to include sub-views into the calling view.
This can be useful for partial segments that you might want to reuse on multiple
pages (such as some sort of user panel, a form, etc.), or just to further separate
code and make things easier for you to work with.
Like with any view, you do not need to include the ".view.lua" suffix.
Example:
@extends('template')
@beginsection('content')
<div class="panel">
@include('panel')
</div>
@endsection
#@foreach()
@foreach(table tableName as string indexName, string viewName) { ... }
The @foreach directive allows you to iterate over a table of items. Essentially, it's a for-loop
You should pass this directive 3 items in a special format: tableName as indexName,valueName.
'tableName' is of course the table to iterate over while 'indexName' and 'valueName' are the name
of the variables to use for the key/value of each iteration.
Example:
<table>
<tr>
<td>Key</td><td>Value</td>
</tr>
<tr>
@foreach(dataTable as index,value)
{
<td>{{ index }}</td><td>{{ value }}</td>
}
</tr>
</table>
#@for()
@for(string variableName = number startCount, number endCount) { ... }
Like @foreach except it iterates from 'startCount' to 'endCount'.
For example, for(count = 1, 10) would iterate 10 times, with 'count' incrementing from 1 to 10.
Example:
@for(count = 1, totalCount)
{
{{ count }}<br />
}
#@if()
@if(condition) { ... }
@if(condition) { ... } else { ... }
A standard if statement, to conditionally show content. Optionally you may also have an else statement.
Example:
@if(someVariable)
{
<p>The statement was true</p>
}
else
{
<p>The statement was false</p>
}
@if( 10 > 5 )
{
<p>Pretty sure 10 is greater than 5</p>
}
#Displaying Data
{{ statement }}
{{! statement !}}
{{# statement #}}
Double curly braces can be used to output some dynamic content into the view. It will also
automatically escape HTML entities. Any Lua code or variables can be placed in the braces.
The {{! statement !}} variation can be used to not escape HTML entities. Use this
if you are trying to output a string that might contain data you want to actually be rendered
as HTML.
The {{# statement #}} variation can be used to run a statement without displaying any of its output.
Example:
<p>Display a string: {{ someString }}</p>
<p>{{ if(true) then return 'This statement is true!'; else return 'This statement is false!' end }}</p>
<p>Display raw string: {{! "Contains <b>HTML</b>" !}} <br />
Would actually display the text "Contains HTML"; HTML would be bold.</p>
<p>Run w/ no display: {{# count = count + 1 #}}</p>
#Responses
Controllers are expected to return a Response object; View:make() renders
the view and returns it as a Response for you. In this context, a Response is simply an object
that pairs an HTTP status code to an HTML string which should be sent to the requesting client.
You may opt to create a Response rather than rely on View:make() if you want more control over
the status code or just to return a simple HTML message. For example, you might want to show
a custom error message.
Example:
-- Route for http://127.0.0.1:8080/store/item/xxxx where xxxx is the name of an item to get info on
function StoreController:getItem(request)
local itemname = request:uriSegment(3, nil);
-- If an itemname wasn't specified or if we couldn't find it in the itemtable, show an error
if( not itemname or not itemtable[itemname] ) then
return Response(404, "<h1>Not Found</h1><p>Sorry, we couldn't find the item for you :(</p>");
end
local data = {
item = itemtable[itemname],
};
return View:make('item_details.view.lua', data);
end
Page last updated at 2018-10-08 19:30:49