I have this server in python:
class MyServer(SimpleHTTPRequestHandler):
def do_POST(self):
...
some code here
...
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'POST')
self.send_header('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers')
self.end_headers()
self.send_response(200)
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
But when I try to make a request in my js code in chrome, I get this error: POST http://localhost:8080/ net::ERR_INVALID_HTTP_RESPONSE
I have read that it is cors problem and I need to add the headers Access-Control-Allow-Origin, and I did and it didn't help.
Anyone has any idea how to fix it?
You need to call send_response first, then your custom send_header calls, then end_headers, then send whatever data you are sending. SimpleHTTPRequestHandler is extremely primitive, and expects you to understand the HTTP protocol.
Related
So, what I'm trying to do is use a Digital Ocean droplet as an api for an application hosted on a different server. Currently, I'm just developing so this server is from my localhost:3000.
On my client side code (JavaScript) I have:
handleSendData = () => {
const request = new XMLHttpRequest()
request.open('POST', 'http://my-droplet-ip/api/create-image')
request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8')
request.setRequestHeader('Access-Control-Allow-Origin', 'http://my-droplet-ip')
request.send(JSON.stringify(data-object))
}
Finally, in my Laravel application (Laravel Framework 5.8.18) I have a route under routes/api.php:
Route::post('/create-image', 'CreateData');
And I have a controller CreateData.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class CreateImage extends Controller
{
/**
* Handle the incoming request.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
return response('Hello World', 200)
->header('Content-Type', 'application/json; charset=UTF-8')
->header('Access-Control-Allow-Origin', '*');
}
}
The problem is when I try to run this request, I get a CORS error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://my-droplet-ip/api/create-image. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
Under the networking tab I get a 404 not found and the Access header is not there in the response.
Any thoughts out there on this issue?
There is a simple solution to this and it is ok for testing on you local machine.
You could just put in index file to allow cors, but I suggest you building middleware for this.
Here is a link it is explained really nice:)
https://medium.com/#petehouston/allow-cors-in-laravel-2b574c51d0c1
You can put also this in index.php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
Hope it helps :D
I have a simple Java web service that handles my SQL method with Jersey. The HTTP call is from an Angular project. GET and POST are working fine, but when I try a DELETE, I get an HTTP 405 error.
This is how I call the method in Angular:
deletaDados(id){
this.service.deleteData(id)
}
+
deleteData(id){
return this.http.delete(`${this.api}/${id}`).subscribe((res) => {
});;
}
And this is the DELETE Java method:
#DELETE
#Path("{id}/")
public Response delete(#PathParam("id") long id) throws SQLException, ClassNotFoundException{
ofertaDAO dao = new ofertaDAO();
dao.delete(id);
return Response
.status(200)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600")
.entity(id)
.build();
}
Seem right... No idea why I'm getting the 405 error.
If the DELETE coming from your frontend JavaScript code is a cross-origin request, then your browser (automatically on its own) will send a CORS preflight OPTIONS request first.
So it seems like the 405 you’re seeing may be the response to that preflight OPTIONS request — and if so, then you’ll need to have an explicit OPTIONS handler in your server-side Java code.
That OPTIONS handler should just send back a response with a 200 status and the necessary CORS headers, with no response body; so, something like this:
#OPTIONS
public Response options(){
return Response
.status(200)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600")
.build();
}
Incidentally, also note that for the Access-Control-Max-Age response header there’s probably not much point in setting a value that’s any greater than 600 (10 minutes) — because if you do set a value greater than that for it, Chrome will just clamp it to 600 anyway.
And as far as why you don’t also run into that 405 when you send a GET or POST from your frontend JavaScript code, the reason is that the GET and POST requests your code is sending don’t have characteristics (such as custom headers) that’ll trigger your browser to do a preflight. But cross-origin DELETE requests always trigger browsers to do a preflight.
I have a web app using Python Django and Dojo framework.
I wanna send a PUT request from Dojo (using dojo/request) to server Django but when server receives a request, the data within are empty and validate Invalid.
BUT when I change method from PUT to POST, it's work correctly.
Here is my code:
_save: function(data){
var idForm = "editForm" + this.id;
var value = dijit.byId(idForm).get('value');
console.log(value);
request.put("/api/guestbook/"+this.bookName+"/greeting/"+this.id+"/", {
data: {
book_name: this.bookName,
message: value.message
},
headers: { "X-CSRFToken": _cookie('csrftoken') }
}).then(lang.hitch(this, function(text){
}));
},
And in Django:
def put(self, request, *args, **kwargs):
form = self.get_form(self.form_class)
logging.warning(form)
logging.warning(request.PUT)
if form.is_valid():
logging.warning("This form is VALID")
else:
logging.warning("This form is INVALID!!!")
Anyone can help me?
Thanks for help!
I found the way to receive PUT method below:
def put(self, request, *args, **kwargs):
request.PUT = QueryDict(request.body)
form = self.form_class(request.PUT)
if form.is_valid():
logging.warning("This form is VALID")
else:
logging.warning("This form is INVALID")
This is ok :)
Thanks all!
I'm guessing from your X-CSRFToken header that you are doing cross domain requests, i.e. CORS.
If you look in your browser's console, you'll probably see an OPTIONS request being sent to the server. This is called a "preflight request", and your server needs to respond with CORS headers telling the browser that it's okay to make the cross domain PUT request.
In your case, you want the server to respond with headers similar to:
Access-Control-Allow-Origin: http://your-site-hostname-and-port
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-CSRFToken
Simple POST requests do not need the preflight OPTIONS request, that's probably why it works. A pretty good tutorial on html5rocks here.
I am a newbie to Bottle and sort of to Python as well and I am trying create an app whenever I click a button, an AJAX is fired and POST a json to server and store it using SQLite.
However, at current stage, I am trying to figure out how do I successfully received the data in the server.
On the client side,
I have the following send_data function written in JavaScript.
function send_data(feedback) {
$.ajax({
url: "/feedback",
type: "POST",
data: JSON.stringify(feedback),
contentType: "application/json",
success: function() {
alert("Feedback successfully stored in the server!");
},
error: function() {
alert("Feedback failed to store back in the server!");
},
}
The passed in parameter feedback looks something like {"id1": 1, "id2": 2}.
On the server side, I have a feedback.py file and the code is
from bottle import request, route, run
#route('/feedback', method='POST')
def feedback():
comments = request.json
print comments
run(host='localhost', port=8080)
Right now, I just want to check if I have received the data successful. But everytime, when I click that botton, I receive the following error
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/feedback. This can be fixed by moving the resource to the same domain or enabling CORS.
OPTIONS http://localhost:8080/feedback [HTTP/1.0 405 Method Not Allowed 2ms]
I am not sure if it's because I am not using the element <form>. That botton is technically just an image. Every time I click that image, send_data() function is fired.
Anyone can help? I really appreciate it. Thank you!
Cross Origins Requests are restricted as a security measure by the browser. It is possible to overcome that by setting an Access-Control-Allow-Origin header. You can can use bottle-cors or create a decorator like the following on your server code (Python/bottle):
def enable_cors(fn):
def _enable_cors(*args, **kwargs):
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
if request.method != 'OPTIONS':
# actual request; reply with the actual response
return fn(*args, **kwargs)
return _enable_cors
and then with your example:
from bottle import request, route, run
#enable_cors
#route('/feedback', method='POST')
def feedback():
comments = request.json
print comments
run(host='localhost', port=8080)
Note that it's best to allow specific origins instead of using *.
You can read more about Cross-Origin Resource Sharing (CORS) here
You can make this work by disabling the Cross-origin restrictions.
On safari, open the Develop panel, and check disable cross-origin restrictions, or use a browser like Firefox.
I'm using webapp2 in a google app engine app. Basically I have built a REST API and I am trying to connect to it from a javascript ajax client. The problem I am facing is in how to correctly implement the Access-Control-Allow-Origin header thing. I have a solution that works but looks pretty clunky to me. Can anyone suggest a better way?
Here is my current solution:
in my main routing file i have:
webapp2.Route(r'/<v>/logins/simple',
handler=controllers.login_c.LoginController,
name='simple_login', handler_method='simple_login',
methods=['GET', 'POST', 'OPTIONS']),
and then in the controller:
class LoginController(webapp2.RequestHandler):
def simple_login(self, v):
self.response.headers.add_header('Access-Control-Allow-Origin', '*')
self.response.headers.add_header('Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept')
self.response.headers.add_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE')
self.response.headers.add_header('Content-Type', 'application/json')
if not self.request.__str__().startswith("OPTIONS"):
...more code
But this solution means i have to duplicate the headers thing in each controller. Can't I have something to catch all the OPTIONS requests?
Was going over this today, and the OP's question:
Can't I have something to catch all the OPTIONS requests?
...is one that I had, too. I created a wrapper class that my restful endpoint classes could inherit from, and it includes a default OPTIONS handler. This can also be done in the dispatch method as well, as demonstrated in this SO post.
main.py
import webapp2
from controllers import widgets_controller
APP = webapp2.WSGIApplication([
('/rest/widgets/all', widget_controller.All),
('/rest/widgets/create', widget_controller.Create),
], debug=True)
widgets_controller.py
class Create(rest.RestHandler):
def post(self):
# We use this for any POST, PUT, and DELETE, where necessary.
self.decorateHeaders();
# Handle the rest of the request here...
rest_controller.py
class RestHandler(webapp2.RequestHandler):
def decorateHeaders(self):
"""Decorates headers for the current request."""
self.response.headers.add_header('Access-Control-Allow-Origin', '*')
self.response.headers.add_header('Access-Control-Allow-Headers', 'Authorization')
self.response.headers.add_header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE')
def options(self):
"""Default OPTIONS handler for the entire app."""
self.decorateHeaders()
Why route all to a single method: simple_login. Why not use a def options(self) in your handler?
I use something like:
class RPCHandler(webapp.RequestHandler):
def options(self):
self.response.headers['Access-Control-Allow-Origin'] = '*'
self.response.headers['Access-Control-Allow-Headers'] = '*'
self.response.headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
def post(self):
self.response.headers['Access-Control-Allow-Origin'] = '*'
self.response.headers['Access-Control-Allow-Headers'] = '*'
self.response.headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
.... more code to handle the rpc ....
def main():
app = webapp.WSGIApplication([
('/', MainPage),
('/rpchr', RPCHandler),
], debug=True)
util.run_wsgi_app(app)
if __name__ == '__main__':
main()
Example taken from some old webapp Python 2.5 application for handling json requests. But it is working fine for a couple of years now.
from app.yaml:
- url: /rpchr
script: main.py
secure: always
- url: /
script: main.py
secure: always
login: admin
But now we have cloud endpoints.