Where is the correct place to enable CORS?
Asked Answered
U

2

1

I'm using Spyne (the example "hello world" code) to make a webservice that produces some json data and then I'm trying to consume this data in javascript code in client's browser.

When I go to the address http://localhost:8000/say_hello?name=Dave&times=3 I get the following output:

["Hello, Dave", "Hello, Dave", "Hello, Dave"]

That's why I think there is nothing to do with the server (it works as expected).

I use the following code to get the data from this webservice:

<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script src="jquery-1.11.1.min.js" ></script>
    <script>
    var request_url = 'http://localhost:8000/say_hello?name=Dave&times=3';
    $.ajax( {
      type:'Get',
      url:request_url,
      dataType: "jsonp",                
      crossDomain : true,
      success:function(data) {
    alert(data);
      },
      error: function()
      {
      alert("fail");
      },

    });
  </script>
  </body>
</html>

Then I get the "fail" popup.

As I searched the net, all I could find was a setting to be made on the server side as follows:

Add following header in the server: 

  Header set Access-Control-Allow-Origin *
  1. If any header setting must be changed on server side, how can I do that?
  2. If there is no need to change any server side settings, what should be the correct client side code?

EDIT

Here is the last version of both python and javascript code:

HTML:

<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script src="jquery-1.11.1.min.js" ></script>
    <script>
    var request_url = 'http://localhost:8000/say_hello?name=Dave&times=3';
    var jdata = 'none'
    $.ajax( {
      type:'Get',
      url:request_url,
      dataType: "html",                
      crossDomain : true,
      success:function(data) {
    alert(data);
      },
      error: function()
      {
      alert("fail");
      },

    });
</script>
  </body>
</html>

Python:

#!/usr/bin/env python
# encoding: utf8

'''
This is a simple HelloWorld example to show the basics of writing a Http api
using Spyne. Here's a sample:

$ curl http://localhost:8000/say_hello?name=Dave\&times=3
["Hello, Dave", "Hello, Dave", "Hello, Dave"]
'''


import logging

from spyne.application import Application
from spyne.decorator import srpc
from spyne.protocol.json import JsonDocument
from spyne.protocol.http import HttpRpc
from spyne.service import ServiceBase
from spyne.model.complex import Iterable
from spyne.model.primitive import UnsignedInteger
from spyne.model.primitive import String
from spyne.server.wsgi import WsgiApplication

class CorsService(ServiceBase):
    origin = '*'

def _on_method_return_object(ctx):
    ctx.transport.resp_headers['Access-Control-Allow-Origin'] = \
                                              ctx.descriptor.service_class.origin

CorsService.event_manager.add_listener('method_return_object',
                                                        _on_method_return_object)


class HelloWorldService(CorsService):

    @srpc(String, UnsignedInteger, _returns=Iterable(String))
    def say_hello(name, times):

        for i in range(times):
            #yield '%s("Hello, %s")' % (callback, name)
            yield {"name": 'Hello (%d): %s' % (i, name), "address": "%d + %d" % (i, i)}


if __name__=='__main__':
    from wsgiref.simple_server import make_server
    logging.basicConfig(level=logging.DEBUG)

    application = Application([HelloWorldService], 'spyne.examples.hello.http',

          in_protocol=HttpRpc(validator='soft'),

          out_protocol=JsonDocument(ignore_wrappers=True),
      )

    wsgi_application = WsgiApplication(application)

    server = make_server('0.0.0.0', 8000, wsgi_application)

    logging.info("listening to http://127.0.0.1:8000")
    logging.info("wsdl is at: http://localhost:8000/?wsdl")

    server.serve_forever()
Unit answered 1/7, 2014 at 17:40 Comment(0)
T
4

You need add this as the first line of your service implementation:

ctx.transport.resp_headers['Access-Control-Allow-Origin'] = '*'

However, that can get very annoying very fast, so here's a way to properly implement it:

class CorsService(ServiceBase):
    origin = '*'

def _on_method_return_object(ctx):
    ctx.transport.resp_headers['Access-Control-Allow-Origin'] = \
                                              ctx.descriptor.service_class.origin

CorsService.event_manager.add_listener('method_return_object', 
                                                        _on_method_return_object)

So instead of using ServiceBase, you can now use CorsService as parent class to your services to get the CORS header automatically.

Also note that it's more secure to set the header value only to the domain that hosts the Spyne service instead of using a wildcard.

Transnational answered 2/7, 2014 at 11:29 Comment(0)
A
1

No need to add any client side code.

You simply need to add the following to the header of the response sent by the server:

Access-Control-Allow-Origin: *

See http://enable-cors.org/server.html for more info for the various server setups. Not familiar with Spyne but this may help

http://spyne.io/docs/2.10/manual/06_metadata.html#protocol-headers

Agreement answered 1/7, 2014 at 17:45 Comment(4)
I find no way to add this header into the server's response but I'm working on it. But I need to say that I don't believe (yes, believe) this is a server side issue. This seems like a client issue. If it is related with a server side setting, why/how can I access this data via web browser or console (curl localhost...)? They say "it is a security related thing", but how does it help security anyways? I'll try the header setting but then, I'll try manually downloading and parsing the page too.Unit
@Unit What messages are in the browser console when the fail alert pops up? If it is indeed a CORS issue it will be something like "No Access-Control-Allow-Origin header is present on requested resource."Agreement
If I set dataType='html', the cors related warning is shown up: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/say_hello?name=Dave&times=3. This can be fixed by moving the resource to the same domain or enabling CORS. If I set dataType='jsonp', in firebug there is no such error message and there is a JSON tab is shown under firebug->Net->header data, in this tab I can reach "Hello, Dave" data (3 elements). But still "fail" alert pops up.Unit
I couldn't find a way to try this but it seems it's worth to try.Unit

© 2022 - 2024 — McMap. All rights reserved.