How to read body as any valid json? - json

I haven't found the docs for that use case. How can I get the request body, ensure it's a valid JSON (any valid JSON, including numbers, string, booleans, and nulls, not only objects and arrays) and get the actual JSON.
Using Pydantic forces the JSON to have a specific structure.

You can find nearly everything inside the Request object
You are able to get request body with request.json(), which will give you the parsed JSON as dictionary.
from fastapi import Request, FastAPI
#app.post("/dummypath")
async def get_body(request: Request):
return await request.json()
If you want access the body as string, you can use request.body()

The accepted answer is valid as well, but FastAPI provides a built-in way to do that - check the Singular values in body section in docs.
A parameter with the default Body gets all the payload that doesn't match passed Pydantic-typed parameters (the whole payload in our case) and converts it to the dict. In case of invalid JSON, a standard validation error would be produced.
from fastapi import Body, FastAPI
app = FastAPI()
#app.post('/test')
async def update_item(
payload: dict = Body(...)
):
return payload
UPD: Note on ... (Ellipsis) - it allows marking a value as required. Read more in the Required with Ellipsis docs section

If you are confident that the incoming data is "a valid JSON", you can create a simple type annotation structure to receive the arbitrary JSON data.
from fastapi import FastAPI
from typing import Any, Dict, AnyStr, List, Union
app = FastAPI()
JSONObject = Dict[AnyStr, Any]
JSONArray = List[Any]
JSONStructure = Union[JSONArray, JSONObject]
#app.post("/")
async def root(arbitrary_json: JSONStructure = None):
return {"received_data": arbitrary_json}
Examples
1. JSON object
curl -X POST "http://0.0.0.0:6022/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"test_key\":\"test_val\"}"
Response:
{
"received_data": {
"test_key": "test_val"
}
}
2. JSON array
curl -X POST "http://0.0.0.0:6022/" -H "accept: application/json" -H "Content-Type: application/json" -d "[\"foo\",\"bar\"]"
Response:
{
"received_data": [
"foo",
"bar"
]
}
If you are not sure about the content type of the incoming data, better to parse the request body.
It can be done as,
from fastapi import FastAPI, Request
app = FastAPI()
#app.post("/")
async def root(request: Request):
return {"received_request_body": await request.body()}
The advantage of this method is that the body will contain any kind of data, JSON, form-data, multipart-form-data, etc.

from fastapi import Request
async def synonyms__select(request: Request):
return await request.json()
will return a JSON object.

This is an example to print the content of a Request, it will print the json body (if it is json parsable) otherwise just print the raw bytes of the body.
async def print_request(request):
print(f'request header : {dict(request.headers.items())}' )
print(f'request query params : {dict(request.query_params.items())}')
try :
print(f'request json : {await request.json()}')
except Exception as err:
# could not parse json
print(f'request body : {await request.body()}')
#app.post("/printREQUEST")
async def create_file(request: Request):
try:
await print_request(request)
return {"status": "OK"}
except Exception as err:
logging.error(f'could not print REQUEST: {err}')
return {"status": "ERR"}

FastAPI has a JSON encoder.
There are some cases where you might need to convert a data type (like
a Pydantic model) to something compatible with JSON
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
import simplejson as json
class SubmitGeneral(BaseModel):
controllerIPaddress: str
readerIPaddress: str
ntpServer: str
#app.post("/submitGeneral")
async def submitGeneral(data: SubmitGeneral):
data = jsonable_encoder(data)
#data = json.loads(data.json()) # same as above line
print(f"data = {json.dumps(data)}")
# you have to access the properties with brackets, not by dot notation
query = f"update LocalPLC set ControllerIpAddress = '{data['controllerIPaddress']}', ReaderIPAddress = '{data['readerIPaddress']}'"
return {"status": "OK"}

For those of you using BaseModel and want to have a JSON field, you can import Json from pydantic
from fastapi import FastAPI
from pydantic import BaseModel, Json, Field
app = FastAPI()
class MockEndpoint(BaseModel):
endpoint: str = Field(description="API endpoint to mock")
response: Json = Field(description="Example response of the endpoint")
#app.get("/")
async def root():
return {"message": "Hello World"}
#app.post("/mock")
async def mock_request(mock_endpoint: MockEndpoint):
return mock_endpoint

Related

Python read json from site print object

I get this json string from a page:
{"lprice":"8330.1","curr1":"BTC","curr2":"EUR"}
I tried to access the lprice with this code:
import requests
def get_latest_price(api, currencie, real):
CEXIO_API_URL = "https://cex.io/api/last_price/%s/%s" % (currencie.upper(), real.upper())
response = requests.get(CEXIO_API_URL)
response_json = response.json()
return float(response_json['lprice'])
But if I do it like this, I get this error:
File
"/home/malte/Entwicklung/cryptoprice/build/all/app/install/qml/cryptoprice.py",
line 16, in get_latest_price
return float(response_json['lprice'])
KeyError: 'lprice'
I assume that your
response_json is your json-string {"lprice":"8330.1","curr1":"BTC","curr2":"EUR"}
Then it should work if you convert the json string into a dictionary with the loads function
import requests
import json
def get_latest_price(api, currencie, real):
CEXIO_API_URL = "https://cex.io/api/last_price/%s/%s" % (currencie.upper(), real.upper())
response = requests.get(CEXIO_API_URL)
response_json = response.json()
response_json = json.loads(response_json)
return float(response_json['lprice'])

How do I make a cURL request to zomato api?

I just began exploring APIs. This is my code so far. For locu API this works but for Zomato they use curl header request which I don't know how to use. Could someone guide or show me how?
import json
import urllib2
Key = 'Zomato_key'
url = 'https://developers.zomato.com/api/v2.1/categories'
json_obj = urllib2.urlopen(url)
data = json.load(json_obj)
print data
By looking at the Zomato API docs, it seems that the parameter user-key has to be set in the header.
The following works:
import json
import urllib2
Key = '<YOUR_ZOMATO_API_KEY>'
url = "https://developers.zomato.com/api/v2.1/categories"
request = urllib2.Request(url, headers={"user-key" : Key})
json_obj = urllib2.urlopen(request)
data = json.load(json_obj)
print data
If you want a more elegant way to query APIs, have a look at requests module (you can install using pip install requests).
I suggest you the following:
import json
import requests
Key = <YOUR_ZOMATO_API_KEY>'
url = "https://developers.zomato.com/api/v2.1/categories"
if __name__ == '__main__':
r = requests.get(url, headers={'user-key': Key})
if r.ok:
data = r.json()
print data
NB: I suggest you remove your Key from StackOverflow if you care about keeping it to yourself.
this didn't work for me can u suggest some other method for me.
-->the code when tried to compile is taking long time and returning an traceback error in request method that is in built
but curl command is working
curl -X GET --header "Accept: application/json" --header "user-key: c5062d18e16b9bb9d857391bb32bb52f" "https://developers.zomato.com/api/v2.1/categories"

Encoding JSON as Base64 Before HTTP POSTing It (in Haskell)

Problem: I am attempting to POST some JSON to an HTTP endpoint that only accepts Base64 encoding.
Code Sample: Here is a code sample which successfully posts without Base64 encoding:
{-# LANGUAGE OverloadedStrings #-}
module Lib where
import Data.Aeson (encode, object, (.=))
import qualified Data.ByteString.Lazy.Char8 as L8
import Network.HTTP.Client
import Network.HTTP.Client.TLS
import Network.HTTP.Types.Status (statusCode)
import qualified Data.ByteString.Base64 as B64
postJSON :: IO ()
postJSON = do
manager <- newManager tlsManagerSettings
-- Nested JSON object to POST:
let requestObject = object
[ "event" .= ("App launched." :: String)
, "properties" .= object [ "distinct_id" .= ("user" :: String)
, "token" .= ("f793bae9548d8e123cef251fd81df487" :: String)
]
]
initialRequest <- parseRequest "http://api.mixpanel.com/track"
let request = initialRequest
{ method = "POST"
, requestBody = RequestBodyLBS $ encode requestObject
, requestHeaders =
[ ("Content-Type", "application/json; charset=utf-8")
]
}
response <- httpLbs request manager
putStrLn $ "The status code was: "
++ show (statusCode $ responseStatus response)
L8.putStrLn $ responseBody response
Attempt: In order to send the JSON as Base64 encoded, I tried replacing requestBody = RequestBodyLBS $ encode requestObject with requestBody = RequestBodyLBS $ Data.Bytestring.Base64.encode (encode requestObject), but I get a type error. So how do I encode the JSON as Base64 for this HTTP POST?
B64.encode here is a function from strict ByteString to strict ByteString (you'll need to hover over the type name in the haddocks to see this if you're just browsing), while Aeson.encode returns a lazy bytestring (from the Data.ByteString.Lazy module). These are two distinct types although they have the same name.
You probably have to do something like:
...
requestBody = RequestBodyLBS $ L8.fromStrict $ Data.Bytestring.Base64.encode (L8.toStrict $ encode requestObject)
I have two things to add to jberryman's answer.
First, if (as it now appears) you're going to be putting this in a query string, you need to make sure you don't just use a base64 encoded bytestring, but instead use a base64 url-encoded bytestring. So don't use Data.Bytestring.Base64 (as jberryman linked to), but rather Data.Bytestring.Base64.URL (here).
Second, while he pointed you in the right direction on the Base64 encoding part, it seems you're still hung up on setting the querystring. For that, you should check out the setQueryString function in the http-client library you're already using (link here).
That function has the signature:
setQueryString :: [(ByteString, Maybe ByteString)] -> Request -> Request
So if you're base64 encoded bytestring is built like this
let urlEncodedBytestring = Data.Bytestring.Base64.URL.encode . L8.toStrict $ encode requestObject
and if you're attemtping to set the data key in the querystring of your request, then you'll probably want:
let requestWithQueryStringSet = setQueryString [("data", (Just urlEncodedBytestring))] request

How to get JSON data in an Odoo controller using type='json'?

A few days ago I did a similar question here: How to get JSON data in an Odoo controller?
But now, I need to create a controller which receives only JSON data. So, I am doing the request from a Python console, this way:
import requests
import json
url = 'http://localhost:8069/odoo/test'
headers = {'Content-Type': 'application/json'}
data = {
'name': 'Jane',
'email': 'jane.doe#gmail.com',
}
data_json = json.dumps(data)
r = requests.post(url=url, data=data_json, headers=headers)
I have created a controller which listens to http://localhost:8069/odoo/test, this way:
import openerp.http as http
from openerp.http import Response
import logging
_logger = logging.getLogger(__name__)
class WebFormController(http.Controller):
#http.route('/odoo/test', type='json',
auth='public', methods=['POST'], website=True)
def index(self, **args):
_logger.info('CONNECTION SUCCESSFUL')
_logger.info(args)
name = args.get('name', False)
email = args.get('email', False)
_logger.info(name)
_logger.info(email)
if not name:
Response.status = '400 Bad Request'
return '{"response": "OK"}'
The problem is that I am receiving an empty JSON in the controller. I can read CONNECTION SUCCESFUL in the log, with no error, but when I show args, I get {}, and obviously due to that, False when writing name and email.
If I pass the data as a Python dictionary or as a string, I get the following error:
Invalid JSON data: 'name=Jane&email=jane.doe%40gmail.com' or
Invalid JSON data: "{'name': 'Jane', 'email': 'jane.doe#gmail.com'}" respectively.
If I modify type='json' and I write type='http' instead, I get the following error:
Function declared as capable of handling request of type 'http' but called with a request of type 'json'.
I have read that may be this could be solved if the request is sent using the parameter json instead of data, this way:
r = requests.post(url=url, json=data_json, headers=headers)
Unfortunately, the server which is going to make the request has an old operating system which cannot update the python-requests package, so I cannot use json parameter since it did not exist at the version installed in that server.
Please, can anyone help me? I need get JSON data in the controller, not a string neither Python dictionaries.
You have just forgotten to put your data inside the params keywords:
Use this correct syntax :
data = {"params": dict(key="value")}
data = {
"params": {
"name":"prakashsharma",
"email":"prakashsharmacs24#gmail.com",
"phone":"+917859884833"
}
}
Please don't forget to use json.dumps(data) and 'Content-Type': 'application/json' while requesting a resource in json format.
I am damn sure your issue will be solved after using this one my friend... cheers :)!!
You can use below format for a POST request
{
"params" : {
"name" : "Order/1/18",
"session_id" : 1,
"customer_count" : 2,
"partner_id" : 9,
"lines": [
{
"product_id": 37,
"qty" : 2,
"price_unit" : 2,
"discount" : 10
}
],
"pos_reference" : 2,
"note" : "This is a test note"
}
}
Content type must be application/json
How odoo route will handle request ?
Route will help creating a POS order in odoo [POST]
#http.route(['/api/v1/resources/<string:api_key>/pos_order'],
auth="public",
website=False,
type="json",
csrf=False,
methods = ['POST'])
def create_update_pos_order(self, api_key=None, **kwargs):
print(kwargs.get('name')) -> Order/1/18
God Bless Forvas::
But for more clearity:
if you want to test through cURL:
curl -i -X POST -H "Content-Type: application/json" -d '{"params": {"name":"prakashsharma","email":"prakashsharmacs24#gmail.com","phone":"+917859884833"}}' 'http://localhost:8069/web/yourlistoner/'
if you want to test through python request:
import requests
headers = {
'Content-Type': 'application/json',
}
data = '{"params": {"name":"prakashsharma","email":"prakashsharmacs24#gmail.com","phone":"+917859884833"}}'
requests.post('http://localhost:8069/web/yourlistoner/', headers=headers, data=data)
the function in odoo will be something like
from odoo import http
import json
class YourClass(http.Controller):
#http.route('/web/yourlistoner/', type='json', auth="none", methods=['POST'],cors="*", csrf=False)
def listoner(self, **kw):
print http.request.params
print "lllllllllllllllllllll"
return json.dumps({"result":"Success"})

Working with JSON and Django

I am new to Python and Django. I am an IT professional that deploys software that monitors computers. The api outputs to JSON. I want to create a Django app that reads the api and outputs the data to an html page. Where do I get started? I think the idea is to write the JSON feed to a Django model. Any help/advice is greatly appreciated.
Here's a simple single file to extract the JSON data:
import urllib2
import json
def printResults(data):
theJSON = json.loads(data)
for i in theJSON[""]
def main():
urlData = ""
webUrl = urllib2.urlopen(urlData)
if (webUrl.getcode() == 200):
data = webUrl.read()
printResults(data)
else:
print "Received error"
if __name__ == '__main__':
main()
If you have an URL returning a json as response, you could try this:
import requests
import json
url = 'http://....' # Your api url
response = requests.get(url)
json_response = response.json()
Now json_response is a list containing dicts. Let's suppose you have this structure:
[
{
'code': ABC,
'avg': 14.5,
'max': 30
},
{
'code': XYZ,
'avg': 11.6,
'max': 21
},
...
]
You can iterate over the list and take every dict into a model.
from yourmodels import CurrentModel
...
for obj in json_response:
cm = CurrentModel()
cm.avg = obj['avg']
cm.max = obj['max']
cm.code = obj['code']
cm.save()
Or you could use a bulk method, but keep in mind that bulk_create does not trigger save method.