In the time since I started writing this series I've learned a lot about REST and am continuing to learn more. While there is nothing intentionally incorrect in these blog posting, after reading Roy Fielding's thesis I'm not satisfied with the coupling of the technologies in this example, and believe that Dr. Fielding's work describes a simpler and much more elegant solution. Use these blogs with that in mind and I will be back when I have an example of REST with Python that I feel best represents Dr. Fielding's idea.
While working on my latest project I discovered a third party package called pyramid_handlers that allows you to use Pylons style controllers for your views. The "action" decorator supplied with the pyramid_handlers package allows you to provide a 'request_method' argument to the handler methods, explicitly defining which HTTP methods the handler will respond to, and what method with respond to the HTTP method.
config.add_handler('home','/',handler=Home,action='read')
The above code maps the site root to the Home handler, with the 'read' action.
class Home(object):
def __init__(self, request):
self.request = request
@action(name='read',request_method="GET",renderer="json")
def get(self):
return dict(message=u"Hello World!")
The above code provides a very satisfying way of separating out what code responds to which HTTP method without having to code decision logic that checks the value of 'request.method'. The home page is a read only page and the only HTTP method that '/' should respond to is "GET". All other HTTP methods should return a 405 Method Not Allowed response, and pyramid provides custom Response objects for the purpose.
class Home(object):
def __init__(self, request):
self.request = request
@action(name='read',request_method="GET",renderer="json")
def get(self):
return dict(message=u"Hello World!")
@action(name='read',request_method="POST")
def post(self):
raise HTTPMethodNotAllowed()
Since HTTPMethodNotAllowed is also an Exception I raise it instead of returning, I've gotten into debates over whether raising is the correct way to generate the response. I believe that in python raising is the idiomatically correct way to generate the response. The python language emphasizes "leap before you look" and "forgiveness is easier to get than permission" programing paradigms, and exception handling and raising is an important part of that. From my personal experience encountering a failure mode in a view handler and trying to snake execution through to a return is difficult. Conditional checks of values are necessary and quite often a None value exists for one of your locals which will raise an AttributeError if you don't "look before you leap", obscuring the original failure mode and making the issue harder to track down. So my opinion is just raise the HTTP exception at the point that the failure mode occurs, do as much clean up as you can within reason, but don't defer returning the response for too long.
With view handlers it is much easier to explicitly use the HTTP methods and provide the correct HTTP status codes in your responses than if you are using view functions. A final note: for most servers the HTTP methods other than GET and POST are blocked, and you will need to tunnel these methods through POST if you want to use them explicitly. You can see an example of tunneling methods through POST here.
No comments:
Post a Comment