Index: python/webiopi/decorators/rest.py
===================================================================
--- python/webiopi/decorators/rest.py	(revision 1245)
+++ python/webiopi/decorators/rest.py	(working copy)
@@ -1,3 +1,5 @@
+from webiopi.utils.types import M_PLAIN
+
 def request(method="GET", path="", data=None):
     def wrapper(func):
         func.routed = True
@@ -7,7 +9,7 @@
         return func
     return wrapper
 
-def response(fmt="%s", contentType="text/plain"):
+def response(fmt="%s", contentType=M_PLAIN):
     def wrapper(func):
         func.format = fmt
         func.contentType = contentType
@@ -14,6 +16,11 @@
         return func
     return wrapper
 
-def macro(func):
-    func.macro = True
-    return func
+def macro(method="POST", action ="", contentType=M_PLAIN):
+    def wrapper(func):
+        func.macro = True
+        func.method = method
+        func.action = action or func.__name__
+        func.contentType = contentType
+        return func
+    return wrapper
Index: python/webiopi/protocols/http.py
===================================================================
--- python/webiopi/protocols/http.py	(revision 1245)
+++ python/webiopi/protocols/http.py	(working copy)
@@ -17,11 +17,13 @@
 import codecs
 import mimetypes as mime
 import logging
+import json
+import cgi
 
 from webiopi.utils.version import VERSION_STRING, PYTHON_MAJOR
 from webiopi.utils.logger import info, exception
 from webiopi.utils.crypto import encrypt
-from webiopi.utils.types import str2bool
+from webiopi.utils.types import str2bool, M_PLAIN, M_JSON, M_URLENC, M_MULTIPART
 
 if PYTHON_MAJOR >= 3:
     import http.server as BaseHTTPServer
@@ -120,7 +122,7 @@
         self.send_header("WWW-Authenticate", 'Basic realm="webiopi"')
         self.end_headers();
     
-    def sendResponse(self, code, body=None, contentType="text/plain"):
+    def sendResponse(self, code, body=None, contentType=M_PLAIN):
         if code >= 400:
             if body != None:
                 self.send_error(code, body)
@@ -207,23 +209,29 @@
                 else:
                     params[s] = None
         
-        compact = False
-        if 'compact' in params:
-            compact = str2bool(params['compact'])
+        try:
+            data = None
+            if 'content-type' in self.headers:
+                ctype, pdict = cgi.parse_header(self.headers.get('content-type'))
+                if ctype == M_MULTIPART:
+                    data = cgi.parse_(self.rfile, pdict)
+                else:
+                    length = int(self.headers.get('content-length'))
+                    if length > 0:
+                        charset = self.headers.get_content_charset()
+                        qs = self.rfile.read(length).decode(charset)
+                        if ctype == M_JSON:
+                            data = json.loads(qs)
+                        elif ctype == M_URLENC:
+                            data = cgi.parse_qs(qs, keep_blank_values=True)
+                        elif ctype == M_PLAIN:
+                            data = qs
+                        else:
+                            self.sendResponse(415)
+                            return
 
-        try:
             result = (None, None, None)
-            if self.command == "GET":
-                result = self.server.handler.do_GET(relativePath, compact)
-            elif self.command == "POST":
-                length = 0
-                length_header = 'content-length'
-                if length_header in self.headers:
-                    length = int(self.headers[length_header])
-                result = self.server.handler.do_POST(relativePath, self.rfile.read(length), compact)
-            else:
-                result = (405, None, None)
-                
+            result = self.server.handler.processRequest(self.command, relativePath, params, data)
             (code, body, contentType) = result
             
             if code > 0:
@@ -247,3 +255,9 @@
 
     def do_POST(self):
         self.processRequest()
+
+    def do_PUT(self):
+        self.processRequest()
+
+    def do_DELETE(self):
+        self.processRequest()
Index: python/webiopi/protocols/rest.py
===================================================================
--- python/webiopi/protocols/rest.py	(revision 1245)
+++ python/webiopi/protocols/rest.py	(working copy)
@@ -25,8 +25,6 @@
 except:
     pass
 
-MACROS = {}
-
 class RESTHandler():
     def __init__(self):
         self.device_mapping = True
@@ -139,9 +137,36 @@
         
         return (200, response, contentType)
         
-    def do_GET(self, relativePath, compact=False):
+    def processRequest(self, method, relativePath, query, data):
+        result = None
         relativePath = self.findRoute(relativePath)
+
+        for macro in self.macros.values():
+            if macro.method == method and relativePath.startswith(macro.action):
+                kwargs = query.copy()
+                if data:
+                    kwargs.update(data if type(data) == dict else {'data': data})
+                path = relativePath.rstrip('/').split('/')[1:] #Only the parts of the path after the action.
+                if len(path) > 0:
+                    kwargs['path'] = path[0] if len(path) == 1 else path
+                result = macro(**kwargs)
+                response = ("%s" % result) if result else ""
+                result = (200, response, macro.contentType)
+                break
+
+        if result == None:
+            compact = str2bool(query['compact']) if ('compact' in query) else False
+
+            if method == "GET":
+                result = self.do_GET(relativePath, compact)
+            elif method == "POST":
+                result = self.do_POST(relativePath, data, compact)
+            else:
+                result = (405, None, None)
+
+        return result
         
+    def do_GET(self, relativePath, compact=False):
         # JSON full state
         if relativePath == "*":
             return (200, self.getJSON(compact), M_JSON)
@@ -178,38 +203,9 @@
             return (0, None, None)
 
     def do_POST(self, relativePath, data, compact=False):
-        relativePath = self.findRoute(relativePath)
-
         if relativePath.startswith("GPIO/"):
             return self.callDeviceFunction("POST", relativePath)
                 
-        elif relativePath.startswith("macros/"):
-            paths = relativePath.split("/")
-            mname = paths[1]
-            if len(paths) > 2:
-                value = paths[2]
-            else:
-                value = ""
-
-            if mname in self.macros:
-                macro = self.macros[mname]
-
-                if ',' in value:
-                    args = value.split(',')
-                    result = macro(*args)
-                elif len(value) > 0:
-                    result = macro(value)
-                else:
-                    result = macro()
-                     
-                response = ""
-                if result:
-                    response = "%s" % result
-                return (200, response, M_PLAIN)
-                    
-            else:
-                return (404, mname + " Not Found", M_PLAIN)
-                
         elif relativePath.startswith("devices/"):
             if not self.device_mapping:
                 return (404, None, None)
Index: python/webiopi/utils/types.py
===================================================================
--- python/webiopi/utils/types.py	(revision 1245)
+++ python/webiopi/utils/types.py	(working copy)
@@ -3,6 +3,8 @@
 
 M_PLAIN = "text/plain"
 M_JSON  = "application/json"
+M_URLENC = "application/x-www-form-urlencoded"
+M_MULTIPART = "multipart/form-data"
 
 def jsonDumps(obj):
     if logger.debugEnabled():
