I'm trying to send a single observation of a collection of tags to a Web API endpoint. The Web API expects a PUT request with a JSON body that has a few string:float entries.
I'm attempting to use an system.net.httpClient()
instance to do this. Here's my example Ignition script:
import pprint
import json
test_tag = system.tag.browse("[default]KPIs/ModelTags")[5]
body = mycode.build_put_request_body_dict(test_tag)
print "Content to include in body:"
print json.dumps(body, indent=4)
url = mycode.api_url
client = system.net.httpClient()
response = client.put(url, headers={"Content-Type":"application/json"}, data=body)
print "\nResponse from API:"
print response.statusCode
print pprint.pprint(response.getJson())
And the subsequent console output:
Content to include in body:
{
"uf_seawater_inlet_hdr_temp": 152.267133838642,
"reject_pressure_stg_2_psi": 167.244077783062,
"avg_flux_stg_1_gfd": 0.1068650075460458,
"permeate_tds_mgL": 161.253300205294,
"feed_temp_deg_F": 129.80171792201202,
"reject_pressure_stg_1_psi": 152.267133838642,
"uf_diff_pressure": 152.267133838642,
"perm_pressure_psi": 152.267133838642,
"feed_pressure_psi": 183.71871612192402,
"uf_buffer_outlet_flow": 153.76482823308402
}
Response from API:
422
{'detail': [{'input': None,
'loc': [u'body'],
'msg': u'Field required',
'type': u'missing',
'url': u'https://errors.pydantic.dev/2.4/v/missing'}]}
You can see that the Pydantic schema on the Web API is complaining that the body is missing. However, if I manually send a curl
command from the Ignition machine with the same PUT and same body to the web API, I get the expected response.
This seems to only apply to trying to have Ignition send a PUT (or POST) with a body...all the GET requests to the API work fine.
Further info, here is the trace from the web API server when it gets the PUT from a curl
command, note that it received 415 bytes in the body:
TRACE: 172.20.70.56:58233 - HTTP connection made
TRACE: 172.20.70.56:58233 - ASGI [9] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.20.70.55', 80), 'client': ('172.20.70.56', 58233), 'scheme': 'http', 'method': 'PUT', 'root_path': '', 'path': '/model/fouling-factor-first-pass', 'raw_path': b'/model/fouling-factor-first-pass', 'query_string': b'', 'headers': '<...>', 'state': {}}
TRACE: 172.20.70.56:58233 - ASGI [9] Receive {'type': 'http.request', 'body': '<415 bytes>', 'more_body': False}
TRACE: 172.20.70.56:58233 - ASGI [9] Send {'type': 'http.response.start', 'status': 200, 'headers': '<...>'}
INFO: 172.20.70.56:58233 - "PUT /model/fouling-factor-first-pass HTTP/1.1" 200 OK
TRACE: 172.20.70.56:58233 - ASGI [9] Send {'type': 'http.response.body', 'body': '<69 bytes>'}
TRACE: 172.20.70.56:58233 - ASGI [9] Completed
TRACE: 172.20.70.56:58233 - HTTP connection lost
Compared to the trace from a PUT request from the Ignition script, note that it receives 0 bytes in the body therefore it sends back 422 since it didn't match the schema:
TRACE: 172.20.70.56:55738 - HTTP connection made
TRACE: 172.20.70.56:55738 - ASGI [2] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.20.70.55', 80), 'client': ('172.20.70.56', 55738), 'scheme': 'http', 'root_path': '', 'headers': '<...>', 'state': {}, 'method': 'PUT', 'path': '/model/fouling-factor-first-pass', 'raw_path': b'/model/fouling-factor-first-pass', 'query_string': b''}
TRACE: 172.20.70.56:55738 - ASGI [2] Receive {'type': 'http.request', 'body': '<0 bytes>', 'more_body': False}
TRACE: 172.20.70.56:55738 - ASGI [2] Send {'type': 'http.response.start', 'status': 422, 'headers': '<...>'}
INFO: 172.20.70.56:55738 - "PUT /model/fouling-factor-first-pass HTTP/1.1" 422 Unprocessable Entity
TRACE: 172.20.70.56:55738 - ASGI [2] Send {'type': 'http.response.body', 'body': '<132 bytes>'}
TRACE: 172.20.70.56:55738 - ASGI [2] Completed
TRACE: 172.20.70.56:55738 - HTTP connection lost
So I'm at a loss at this point. Appreciate any ideas.