Where is the correct place to enable CORS?

2019-03-02 05:03发布

问题:

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()

回答1:

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.



回答2:

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