1

I would like to write a FastAPI endpoint, with a Swagger page (or something similar) that will accept a non-encoded URL as input. It should preferably use GET, not POST method.

Here's an example of a GET endpoint that does require double-URL encoding.

@app.get("/get_from_dub_encoded/{double_encoded_url}")
async def get_from_dub_encoded(double_encoded_url: str):
    """
    Try https%253A%252F%252Fworld.openfoodfacts.org%252Fapi%252Fv0%252Fproduct%252F7622300489434.json
    """
    original_url = urllib.parse.unquote(urllib.parse.unquote(double_encoded_url))
    response = requests.get(original_url)
    return response.json()

Which generates a Swagger interface as below.

The following PUT request does solve my problem, but the simplicity of a GET request with a form is better for my co-workers.

class InputModel(BaseModel):
    unencoded_url: AnyUrl = Field(description="An unencoded URL for an external resource", format="url")


@app.post("/unencoded-url")
def unencoded_url(inputs: InputModel):
    response = requests.get(inputs.unencoded_url)
    return response.json()

How can I deploy a convenient interface like that without requiring users to write the payload for a PUT request or to perform double URL encoding?

enter image description here

This post has some helpful related discussion, but doesn't explicitly address the FORM solution: How to pass URL as a path parameter to a FastAPI route?

Mark Miller
  • 3,011
  • 1
  • 14
  • 34
  • 1
    Does this answer your question? [How to pass URL as a path parameter to a FastAPI route?](https://stackoverflow.com/questions/72801333/how-to-pass-url-as-a-path-parameter-to-a-fastapi-route) – Chris Dec 30 '22 at 18:15
  • 1
    Related answers can be found [here](https://stackoverflow.com/a/70840850/17865804) and [here](https://stackoverflow.com/a/72068032/17865804) as well. I would also highly suggest you use `httpx` instead of Python `requests` module. Please have a look at [this answer](https://stackoverflow.com/a/73736138/17865804) and [this answer](https://stackoverflow.com/a/74239367/17865804). – Chris Dec 30 '22 at 18:33
  • Thanks for your attention to this question, @Chris – Mark Miller Dec 31 '22 at 19:09
  • You may also have a look at Options 2 & 3 of [this answer](https://stackoverflow.com/a/74338314/17865804). I would also suggest you have a look at MDN's documentation on [Sending form data](https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data#the_method_attribute). – Chris Jan 01 '23 at 08:26

1 Answers1

1

You can use Form instead of query parameters as payload.

from fastapi import FastAPI, Form
import requests

app = FastAPI()

@app.post("/")
def get_url(url: str = Form()):

    """
    Try https://world.openfoodfacts.org/api/v0/product/7622300489434.json
    """
    response = requests.get(url)
    return response.json()

Swagger interface would look like: enter image description here

You'll need to install python-multipart.

Tip: Don't use async endpoint if you are using requests or any non-async library.

  • 1
    Great, I am feeling very optimistic about this. However, when I launch uvicorn, I get this error: " def unencoded_form(url: str = Form()): TypeError: Form() missing 1 required positional argument: 'default'" – Mark Miller Dec 29 '22 at 17:04
  • It does what I was asking for as long as the function signature looks like this: `def unencoded_form(url: str = Form(default="https://world.openfoodfacts.org/api/v0/product/7622300489434.json")):`. I guess I should just do some reading! – Mark Miller Dec 29 '22 at 17:07
  • 1
    I guess we're on a different version. My FastAPI is `0.82.0`. Also, you can just pass `default=None` in that case. – ScriptKiddieOnAComputer Dec 29 '22 at 17:19
  • 1
    This was helpful regarding defaults: https://github.com/tiangolo/fastapi/issues/854 – Mark Miller Dec 29 '22 at 17:19