Verständnisfrage REST API

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mcnill
User
Beiträge: 3
Registriert: Mittwoch 1. Dezember 2021, 16:28

Hallo zusammen,

auch wenn ich mir nicht sicher bin ob das Thema hier richtig aufgehoben ist, habe ich mich doch entschlossen einen Versuch zu wagen...
Falls es nicht ins Forum passt, bitte löschen.

Ein Programm stellt via lokalem webserver eine REST API zur Verfügung gegen die ich Daten austausche.
Die meisten API Aufrufe sind selbsterklärend. An der Übertragung von Dateien an den Server scheitere ich jedoch bislang.
Der Server loggt bei jeder Anfrage das der String Parameter "Name" fehlt und wirft einen 400er aus. :(
Das bedeutet, dass ich die Anfrage falsch erstelle.
-> "name" ist der einzige Pflichtparameter.
-> wohin mit den Binärdaten?
Hier fehlt mir offensichtlich das nötige Hintergrundwissen/ Verständnis für die API Beschreibung.
Vielleicht kann mir hier freundlicherweise jemand auf die Sprünge helfen.

Besten Dank für eure Hilfe!

Mein Versuch den Aufruf zu formulieren:

Code: Alles auswählen

def send_ifc_model():
    ifcfilepath =  "C:\\temp\\Testmodell.ifc"
    with open(ifcfilepath, "rb") as f:
        content = f.read()
    headers = {'content-type': 'application/octet-stream'}
    data = {'name' : content}
    gpl_response  = requests.post(f'{smc_baseurl}/models', headers=headers, data=data)
    print(gpl_response.status_code)
Hier ein Auszug aus der API Doku:

Code: Alles auswählen

post:	
    tags:
        0:	                    "models-api-controller"
    summary:	                "Open IFC Model"
    operationId:	            "openIFCModel"
    consumes:	
        0:	                    "application/octet-stream"
    produces:
        0:	                    "application/json"

    parameters:	
        0:
            name:	            "method"
            in:                	 "query"
            required:   	    false
            type:	            "string"
            enum:
                0:	            "DELETE"
                1:	            "GET"
                2:          	"HEAD"
                3:           	"OPTIONS"
                4:	            "PATCH"
                5:	            "POST"
                6:  	        "PUT"
                7:	            "TRACE"
        1:
            name:       	    "name"
            in:	                "query"
            description:	    "Name of the model"
            required:	        true
            type:	            "string"
        2:
            name:	            "type"
            in:	                "query"
            required:	        false
            type:	            "string"
        3	
            name:	            "url"
            in:	                "query"
            required:	        false
            type:	            "string"
            format:	            "uri"
    responses:	
        200	{…}
        201	{…}
        401	{…}
        403	{…}
        404	{…}
        500	{…}
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mcnill: Wenn andere Aufrufe nach dem Muster funktionieren, dann scheint die API Dir bestimmte Fehler zu verzeihen, denn das was Du `data` nennst und als `data` übergibst, soll laut Dokumentation in die „query“, also nicht in den „body“ der Anfrage sondern in die URL. Bei `requests` wäre das `params`. Womit dann der „body“ sinnvollerweise für die Binärdaten genutzt werden kann.

Bei "name" soll auch der *Name* übergeben werden, nicht der Inhalt der Datei.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mcnill
User
Beiträge: 3
Registriert: Mittwoch 1. Dezember 2021, 16:28

@__blackjack__
Vielen Dank für die Antwort! Die Erklärung hat es gebraucht.
Die anderen Aufrufe funktionieren nach einem anderen Muster...
Wiefolgt klappt der Aufruf nun.

Code: Alles auswählen

def send_ifc_model():
    ifcfilepath =  "C:\\temp\\Testmodell.ifc"
    with open(ifcfilepath, "rb") as f:
        content = f.read()
    headers = {'content-type':'application/octet-stream'}
    params = {'name':'IFCDatei'}
    data = (content)
    gpl_response  = requests.post(f'{smc_baseurl}/models', headers=headers, params=params, data=data)
    print(gpl_response.json())
Respekt! Als Endanwender von Software der ich bin, bekomme ich sonst bunte Bildchen mit Erklärungen. Da ist die API Doku hier schon was anderes.
Merci nochmals!
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

`headers` ist überflüssig, denn das ist der Default, und warum nennst Du `content` in `data` um?
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde noch prüfen ob die Antwort keine Fehlerantwort war. Auch wenn das `json()` dann zwar sehr wahrscheinlich zu einer Ausnahme führt, ist die Ausnahme die sich auf den HTTP-Status bezieht mit mehr Informationen versehen was die Ursache angeht. Ungetestet:

Code: Alles auswählen

import requests
from pathlib import Path


def send_ifc_model():
    response = requests.post(
        f"{smc_baseurl}/models",
        params={"name": "IFCDatei"},
        data=Path(R"C:\temp\Testmodell.ifc").read_bytes(),
    )
    response.raise_for_status()
    print(response.json())
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mcnill
User
Beiträge: 3
Registriert: Mittwoch 1. Dezember 2021, 16:28

@sirius3:
Danke dir für die Hinweise.
headers -> war mir so nicht klar, steht aber eigentlich auch in der Doku von 'requests' wenn man nachliest...
content in data umzubenennen -> klarer Unsinn. :)

@ __blackjack__:
Abermals danke für die Hilfestellung!
rawstrings für Windowspfade,
das 'pathlib' Modul (kannte ich nicht),
die raise_for_status methode.
Wunderbar! :)
Bisher habe ich nur die API Aufrufe zusammengestellt, das Ganze muss noch in ein vollständiges Skript mit Prüfung ob der Server da ist und so weiter.
Antworten