pyopenapi / pyswagger

An OpenAPI (fka Swagger) client & converter in python, which is type-safe, dynamic, spec-compliant.
MIT License
385 stars 89 forks source link

How to set different accept and content-type in the header when call the request #61

Closed LouisZou closed 8 years ago

LouisZou commented 8 years ago

I call the request and found the log like following: pyswagger.core - INFO - request.header: {'Accept': 'application/json'}

But for my restful API, need the special accept and content-type such as "application/my+json", so the request failed because of error media type. how to send the correct type for my request.

mission-liao commented 8 years ago

Hi, pyswagger can only produce request with content-type: application/json, an modification for content-type other than json is still ongoing here.

One thing needs to be clarified before going further: is "application/my+json" a special json, or just a special name in your service for json?

LouisZou commented 8 years ago

Thanks mission-liao, the type "application/my+json" is a special type in our restful service and the server will check the type of request.

I think I can do some customization for this issue by change the code "class SwaggerRequest(object)".

I think I can read the real type from the json file and I need only set this type into header and then send request to my restful api server.

mission-liao commented 8 years ago

The problem is, the way to handle content type in pyswagger is static, and can't be pluggable, which means it can't things like "application/my+json".

A fix would be required for pyswagger to be extensible, One more thing to make sure @LouisZou , so "application/my+json" is actually a different name for json, which is nothing different from how we encode swagger primitives to json?

LouisZou commented 8 years ago

Sure, the "application/my+json" is also a json type but special name in my restful server. I can also deal with it as a normal json format.

mission-liao commented 8 years ago

OK, I think that use case should be covered by that PR.

LouisZou commented 8 years ago

Hi mission-liao, I found this issue is fixed with issue#49. And now how can I use it with the newest pyswagger? just like: req, resp = op(user='Tom') req.mime = ("myjson", "myjson") resp = client.request((req, resp),) -- and then analyze the resp to get response from http server. is this correct?

LouisZou commented 8 years ago

And sorry for I don't understand the sentence req, resp = op(user='Tom') does this sentence will execute the method op? can I use it like "req, resp=op()"? because I will decide how to set the parameter later such as, op(body=mybody) or op(user='Tom'). in fact, my read code will be: client.request(eval("op(" + tagUser +"='Tom'")) -- tagUser is a variable whose value is "user".

mission-liao commented 8 years ago

The design of pyswagger to perform an Operation Object is to return a tuple of (SwaggerReqeust, SwaggerResponse). If you just use it in this form:

client.request(op(user="Tom"))

The tuple of SwaggerRequest, SwaggerResponse is silently pass to client.request. If you need more customization, you need to pass them in this form

req, resp = op(user="Tom")
req.mime .... # any customization
resp.xxxx

client.request((req, resp,)) # pass them as a tuple

The mime part is still under review, and would be merged to next release.

LouisZou commented 8 years ago

Thanks mission-liao. for the req, resp, can I consider them as the meaning of the words. Assume now my restful server need content-type is myjon1 in request and accept is myjon2 in response, so I will write: req.mime("myjon1","") resp.mime("","myjon2") it this correct?

or: req.mime("myjson1","myjson2")

mission-liao commented 8 years ago

No, you can refer to that PR for more info.

req.produce(...)
req.consume(...)
mission-liao commented 8 years ago

This PR is merged in latest build.

LouisZou commented 8 years ago

HI mission, I used the newest revision 0.8.18, when I call the request, it failed with the following message : Could not find codec for "myjson", => it is in the function marshall.

LouisZou commented 8 years ago

I noticed the following code:

class MimeCodec:
    def __init__(self):
        self._codecs = {}
        self.register('text/plain', PlainCodec())
        jsonCodec = JsonCodec()
        self.register('application/json', jsonCodec)
        self.register('text/json', jsonCodec)

I have a new mime type in my definition file and I want to set this type to header and then send request to server. for my curl command ,it like this: curl --header {"Content-Type":"myjson", "Accept":"myjson"}

does need this mime to be registered just like above code?

LouisZou commented 8 years ago

Addition: in the revision 0.8.17, I can fix the code as the following and then I can use "my+json" as content-type and Accept in the header and then send request .

so I also think pyswagger can read the real mime-type for each operation from my definition file and then send request with them.

How can I realize this simply?

mission-liao commented 8 years ago

Hi, the guy helped to implement that feature also provide a guide to register a customized codec. If that one doesn't solve your problem, please help to provide more details about what you want to do.

LouisZou commented 8 years ago

Hi mission-liao, I write my program according to the guide code but the above error occurs. And now I add the following code ,it can work. in site-packages/pyswagger/primitives/codec.py 6 class MimeCodec: 7 def init(self): ... 13 self.register('application/my01+json', jsonCodec) 14 self.register('application/my02+json', jsonCodec)

so the method marshal know my mime type and then the request work well.

but about this issue. I think I don't need to marshal and unmashal my data according to my mime type. I only need to set the Content-Type and Accept in Header of Request. And I think these types should be read from definition file by pyswagger because I specify the consume and produce in my operation.

what do you think?

mission-liao commented 8 years ago

Hi, I will write a test sample for your case tomorrow. Besides that, I can't get your point for:

don't need to marshal and unmashal my data according to my mime type

I can't get your point, any python primitives (dict, list) should be marshalled to some representative form in string when sending to servers.

mission-liao commented 8 years ago

works with sample code:

from pyswagger import SwaggerApp
from pyswagger.primitives import MimeCodec
from pyswagger.primitives.codec import JsonCodec

# init codecs
codecs = MimeCodec()
jc = JsonCodec()
codecs.register('application/my01+json', jc)
codecs.register('application/my02+json', jc)

# init SwaggerApp with customized primitive factory
app = SwaggerApp.load('./swagger.json', mime_codec=codecs)
app.prepare()
op = app.s('/t').get

# make sure my01xxxxx in that 'op'
print op._mime_codec._codecs.keys() # expected ['application/my02+json', 'application/my01+json', 'application/json', 'text/plain', 'text/json']

with sample spec

{
   "swagger":"2.0",
   "host":"http://test.com",
   "basePath":"/v1",
   "paths":{
      "/t":{
         "get":{
            "parameters":[
               {
                  "in":"body",
                  "name":"body",
                  "schema":{
                     "$ref":"#/definitions/MyRequest"
                  }
               }
            ],
            "responses":{
               "default":{
                  "description":"void"
               }
            }
         }
      }
   },
   "definitions":{
      "MyRequest":{
         "properties":{
            "units":{
               "additionalProperties":{
                  "format":"int32",
                  "type":"integer"
               }
            },
            "username":{
               "type":"string"
            }
         }
      }
   }
}
LouisZou commented 8 years ago

Thanks mission. before I add the register sentence in the codec.py, it work well. that's meaning user can also call this method register in self's code.

I will fix my code.

LouisZou commented 8 years ago

hi mission, sorry for so late to test the code. now i used the register code like above and I can print myjson1/2 with the code but the request failed with the exception: Exception:unbound method marshal() must be called with JsonCodec instance as first argument (got Model instance instead)

LouisZou commented 8 years ago

And in my source code 30 codecs.register('application/json',JsonCodec) 31 codecs.register('application/myjson2', JsonCodec) line 30 is ok but line 31 failed with the above exception.

LouisZou commented 8 years ago

Addition, If I add the register code in the file primitives/codec.py: 6 class MimeCodec: 7 def init(self): ... 13 self.register('application/myjson01', jsonCodec) 14 self.register('application/myjson02', jsonCodec)

it work well

mission-liao commented 8 years ago

Could you paste the full trace of that exception?

WillaLuo commented 8 years ago

Hi mission, Here is the full exception. File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/io.py", line 75, in _prepare_body return content_type, self.__op._mime_codec.marshal(content_type, body, _type=_type, _format=_format, name=name) File "/scratch/shuluo/swagger/swagger_sdk/compute-controlplane/virtualenv/lib/python2.6/site-packages/pyswagger/primitives/codec.py", line 29, in marshal return codec.marshal(value, **kwargs) TypeError: unbound method marshal() must be called with JsonCodec instance as first argument (got Model instance instead)

It seems like there is something in this line: app = SwaggerApp.load('./swagger.json', mime_codec=codecs)

mission-liao commented 8 years ago

I guess some code in my sample is wrong, and would only failed when something really loaded.....

"""
should be instance, not class
"""
codecs.register('application/json',JsonCodec)  # Wrong
codecs.register('application/myjson2', JsonCodec) # Wrong

"""
should work in this way
"""
jsonC = JsonCodec()
codecs.register('application/json',jsonC) 
codecs.register('application/myjson2', jsonC)

Sorry for my wrong sample.

WillaLuo commented 8 years ago

Hi, mission, it works now. Thanks very much!

mission-liao commented 8 years ago

It should be fine to close it now, please feel free to reopen it when things go wrong.