Python Tornado - CORS PUT - python

I am facing a problem with Tornado. I have an API endpoint for PUT HTTP Method in Tornado. I also have a web application that sends the request to this API with jQuery and AJAX, but always I get a 405 response because the request is going as HTTP Method OPTIONS.
I understand the way it works and I did configured my Tornado Server to allow it. But even so I having this situation.
Can someone help me?
There is my server code:
class BaseHandler(RequestHandler):
def __init__(self, *args, **kwargs):
super(BaseHandler, self).__init__(*args, **kwargs)
self.set_header('Cache-Control', 'no-store, no-cache, must- revalidate, max-age=0')
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "Content-Type")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS')
Many thanks

You need to add an options handler that just sends the headers with no body:
def options(self):
# no body
self.set_status(204)
self.finish()
See Tornado server: enable CORS requests for a complete code snippet.
Or else just install the tornado-cors package:
pip install tornado-cors
That will add the necessary handlers for you, and ensure the right response headers get sent.

if you don't define put method return 405
class Handler(tornado.web.RequestHandler):
def put(self):
self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "Content-Type")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS')
[I 170205 04:56:35 web:1971] 200 PUT

Related

How to redirect POST request using aiohttp

I'm using python3 and aiohttp http server to handle requests from client.How should I redirect a POST request with body data?
I've tried
aiohttp.web.HTTPFound('/redirect')
it works with GET requests,but what if the request is POST?
You can redirect request with body, headers, etc.
HTTPFound(location, *, headers=None, reason=None, body=None, text=None, content_type=None)
See documentation: https://docs.aiohttp.org/en/stable/web_quickstart.html#exceptions

http post in ionic and python server

i use python json http server and need post json with ionic in this server,but http post method send options type.i need send post type.whats the problem?
Server:
def do_POST(self):
content_len = int(self.headers.getheader('content-length'))
post_body = self.rfile.read(content_len)
self.send_response(200)
self.end_headers()
data = json.loads(post_body)
self.wfile.write(data['id'])
return
ionic http post method:
$http.post(url, JSON.stringify({"id":"1"})).success(function (res) {
console.log("res" + res);
}).error(function (data, status, headers, config) {
console.log(data, status, headers,JSON.stringify (config));
});
Error from python server:
192.168.1.4 - - [10/Mar/2017 02:36:28] code 501, message Unsupported method ('OPTIONS')
192.168.1.4 - - [10/Mar/2017 02:36:28] "OPTIONS / HTTP/1.1" 501 -
This looks like a Cross-Origin Resource Sharing (CORS) preflight request issue.
I would recommend adding a do_OPTIONS method to your class:
def do_OPTIONS(self):
self.send_response(200, "ok")
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With")
This will tell your browser that the POST request has Access-Control.
Another good SO article on this can be found here.

Passing session from template view to python requests api call

I want to make multiple internal REST API call from my Django TemplateView, using requests library. Now I want to pass the session too from template view to api call. What is the recommended way to do that, keeping performance in mind.
Right now, I'm extracting cookie from the current request object in template view, and passing that to requests.get() or requests.post() call. But problem with that is, I would have to pass request object to my API Client, which I don't want.
This the current wrapper I'm using to route my requests:
def wrap_internal_api_call(request, requests_api, uri, data=None, params=None, cookies=None, is_json=False, files=None):
headers = {'referer': request.META.get('HTTP_REFERER')}
logger.debug('Request API: %s calling URL: %s', requests_api, uri)
logger.debug('Referer header sent with requests: %s', headers['referer'])
if cookies:
csrf_token = cookies.get('csrftoken', None)
else:
csrf_token = request.COOKIES.get('csrftoken', None)
if csrf_token:
headers['X-CSRFToken'] = csrf_token
if data:
if is_json:
return requests_api(uri, json=data, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
elif not files:
return requests_api(uri, data=data, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
else:
return requests_api(uri, data=data, files=files, params=params, cookies=cookies if cookies else request.COOKIES,
headers=headers)
else:
return requests_api(uri, params=params, cookies=cookies if cookies else request.COOKIES, headers=headers)
Basically I want to get rid of that request parameter (1st param), because then to call it I've to keep passing request object from TemplateViews to internal services. Also, how can I keep persistent connection across multiple calls?
REST vs Invoking the view directly
While it's possible for a web app to make a REST API call to itself. That's not what REST is designed for. Consider the following from: https://docs.djangoproject.com/ja/1.9/topics/http/middleware/
As you can see a django request/response cycle has quite a bit of overhead. Add to this the overhead of webserver and wsgi container. At the client side you have the overhead associated with the requests library, but hang on a sec, the client also happens to be the same web app so it become s part of the web app's overhead too. And there is the problem of peristence (which I will come to shortly).
Last but not least, if you have a DNS round robin setup your request may actually go out on the wire before coming back to the same server. There is a better way, to invoke the view directly.
To invoke another view without the rest API call is really easy
other_app.other_view(request, **kwargs)
This has been discussed a few times here at links such as Django Call Class based view from another class based view and Can I call a view from within another view? so I will not elaborate.
Persistent requests
Persistent http requests (talking about python requests rather than django.http.request.HttpRequest) are managed through session objects (again not to be confused with django sessions). Avoiding confusion is really difficult:
The Session object allows you to persist certain parameters across
requests. It also persists cookies across all requests made from the
Session instance, and will use urllib3's connection pooling. So if
you're making several requests to the same host, the underlying TCP
connection will be reused, which can result in a significant
performance increase
Different hits to your django view will probably be from different users so you don't want to same cookie reused for the internal REST call. The other problem is that the python session object cannot be persisted between two different hit to the django view. Sockets cannot generally be serialized, a requirement for chucking them into memcached or redis.
If you still want to persist with internal REST
I think #julian 's answer shows how to avoid passing the django request instance as a parameter.
If you want to avoid passing the request to wrap_internal_api_call, all you need to do is do a bit more work on the end of the TemplateView where you call the api wrapper. Note that your original wrapper is doing a lot of cookies if cookies else request.COOKIES. You can factor that out to the calling site. Rewrite your api wrapper as follows:
def wrap_internal_api_call(referer, requests_api, uri, data=None, params=None, cookies, is_json=False, files=None):
headers = {'referer': referer}
logger.debug('Request API: %s calling URL: %s', requests_api, uri)
logger.debug('Referer header sent with requests: %s', referer)
csrf_token = cookies.get('csrftoken', None)
if csrf_token:
headers['X-CSRFToken'] = csrf_token
if data:
if is_json:
return requests_api(uri, json=data, params=params, cookies=cookies, headers=headers)
elif not files:
return requests_api(uri, data=data, params=params, cookies=cookies, headers=headers)
else:
return requests_api(uri, data=data, files=files, params=params, cookies=cookies, headers=headers)
else:
return requests_api(uri, params=params, cookies=cookies, headers=headers)
Now, at the place of invocation, instead of
wrap_internal_api_call(request, requests_api, uri, data, params, cookies, is_json, files)
do:
cookies_param = cookies or request.COOKIES
referer_param = request.META.get['HTTP_REFERER']
wrap_internal_api_call(referer_param, requests_api, uri, data, params, cookies_param, is_json, files)
Now you are not passing the request object to the wrapper anymore. This saves a little bit of time because you don't test cookies over and over, but otherwise it doesn't make a difference for performance. In fact, you could achieve the same slight performance gain just by doing the cookies or request.COOKIES once inside the api wrapper.
Networking is always the tightest bottleneck in any application. So if these internal APIs are on the same machine as your TemplateView, your best bet for performance is to avoid doing an API call.
Basically I want to get rid of that request parameter (1st param), because then to call it I've to keep passing request object from TemplateViews to internal services.
To pass function args without explicitly passing them into function calls you can use decorators to wrap your functions and automatically inject your arguments. Using this with a global variable and some django middleware for registering the request before it gets to your view will solve your problem. See below for an abstracted and simplified version of what I mean.
request_decorators.py
REQUEST = None
def request_extractor(func):
def extractor(cls, request, *args, **kwargs):
global REQUEST
REQUEST = request # this part registers request arg to global
return func(cls, request, *args, **kwargs)
return extractor
def request_injector(func):
def injector(*args, **kwargs):
global REQUEST
request = REQUEST
if len(args) > 0 and callable(args[0]): # to make it work with class methods
return func(args[0], request, args[1:], **kwargs) # class method
return func(request, *args, **kwargs) # function
return injector
extract_request_middleware.py
See the django docs for info on setting up middleware
from request_decorators import request_extractor
class ExtractRequest:
#request_extractor
def process_request(self, request):
return None
internal_function.py
from request_decorators import request_injector
#request_injector
def internal_function(request):
return request
your_view.py
from internal_function import internal_function
def view_with_request(request):
return internal_function() # here we don't need to pass in the request arg.
def run_test():
request = "a request!"
ExtractRequest().process_request(request)
response = view_with_request(request)
return response
if __name__ == '__main__':
assert run_test() == "a request!"

Remove/Rewrite HTTP header 'Server: TwistedWeb'

There is some way to remove HTTP Header 'Server: TwistedWeb/13.1.0' from responses from a Twisted based web application?
You can rewrite any header by calling the request.setHeader method.
class RootPage(Resource):
def getChild(self, name, request):
request.setHeader('server', 'MyVeryOwnServer/1.0')
return OtherResource(name)
The change applies to any resources on your site; you could put it in your Site class. You want that 404 or 500 errors would also return correct header; so you should set it as earlier as possible but not before it is set by twisted itself (in order to overwrite it):
#!/usr/bin/env python
import sys
from twisted.web import server, resource
from twisted.internet import reactor
from twisted.python import log
class Site(server.Site):
def getResourceFor(self, request):
request.setHeader('server', 'Server/1.9E377')
return server.Site.getResourceFor(self, request)
# example from http://twistedmatrix.com/
class HelloResource(resource.Resource):
isLeaf = True
numberRequests = 0
def render_GET(self, request):
self.numberRequests += 1
request.setHeader("content-type", "text/plain")
return "I am request #" + str(self.numberRequests) + "\n"
log.startLogging(sys.stderr)
reactor.listenTCP(8080, Site(HelloResource()))
reactor.run()
Default Server http header is specified in t.w.server.version.
I know this is an old question, but if you would like to remove the server http header. I am talking about the
request.setHeader('Server', 'SomeServer')
This is set by Twisted Web automagically if you don't specify a value. You can remove it by using the inner Headers class. For example,
request.responseHeaders.removeHeader('Server')
This will remove the Server Http Header.

In Pyramid, how can prevent cookies from being set in certain responses?

In Pyramid, using beaker for sessions, how can I make it so certain responses do not include cookies?
Currently if I curl any url on my app I'll get back something like:
HTTP/1.1 200 OK
Server: nginx/1.2.6
Date: Thu, 07 Nov 2013 02:14:45 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 776
Connection: keep-alive
Set-Cookie: beaker.session.id=0a6d945c09884ca29d73bc4ff4d09ff0; expires=Thu, 07-Nov-2013 03:14:45 GMT; httponly; Path=/; secure
I don't need that cookie being set with all requests. For example I'd like to remove it from requests that have the subdomain "api". I tried changing:
def main(global_config, **settings):
session_factory = session_factory_from_settings(settings)
config = Configurator(settings=settings, root_factory=get_root)
config.set_session_factory(session_factory)
return config.make_wsgi_app()
to:
def main(global_config, **settings):
session_factory = session_factory_from_settings(settings)
config = Configurator(settings=settings, root_factory=get_root)
#config.set_session_factory(session_factory)
return MethodOverride(config, session_factory)
class MethodOverride(object):
def __init__(self, config, session_factory):
import copy
self.config = copy.deepcopy(config)
config.set_session_factory(session_factory)
self.application = config.make_wsgi_app()
def __call__(self, environ, start_response):
if "api" == environ['HTTP_HOST'].split('.')[0]:
self.application = self.config.make_wsgi_app()
Which I thought would make it so that the session factory wouldn't be set in those instances and therefore no cookies. I don't understand what's going on with the middleware well enough. I'd also be fine with figuring out a way to make it so Response objects that have an "application/json" mimetype don't include that cookie. Any help would be much appreciated.
One way you could do this is by using an NewResponse subscriber that would modify the outgoing response.
For example:
def new_response_subscriber(event):
request = event.request
response = event.response
if "api" == request.environ['HTTP_HOST'].split('.')[0]:
if 'Set-Cookie' in response.headers:
del response.headers['Set-Cookie']
This would be one way to remove all cookies from all responses. Another way to do it would be to create a new session factory that checks to see if the current URL is an API request, and if so, it doesn't create a session at all.

Categories