Responses
Here, let's take a look into how responses are built in Frappe, and how you may be able to use them in your Frappe apps or scripts.
If you have already gone through the
Router
Documentation
, you might've noticed the
build_response
function that Frappe internally utilizes to build responses
depending on the type of the content. The logic that defines this behaviour is a
part of the module
frappe.utils.response
,
of which
build_response
is the meat and potatoes.
def build_response(response_type=None):
if "docs" in frappe.local.response and not frappe.local.response.docs:
del frappe.local.response["docs"]
response_type_map = {
"csv": as_csv,
"txt": as_txt,
"download": as_raw,
"json": as_json,
"pdf": as_pdf,
"page": as_page,
"redirect": redirect,
"binary": as_binary,
}
return response_type_map[frappe.response.get("type") or response_type]()
The above snippet represents the current implementation of
build_response
which maps different functions that act as handlers for different content types.
Let's take a deeper look into the response handler for the
"download"
response_type
in Frappe v13.
def as_raw():
response = Response()
response.mimetype = (
frappe.response.get("content_type")
or mimetypes.guess_type(frappe.response["filename"])[0]
or "application/unknown"
)
response.headers["Content-Disposition"] = (
f'{frappe.response.get("display_content_as", "attachment")};'
f' filename="{frappe.response["filename"].replace(" ", "_")}"'
).encode("utf-8")
response.data = frappe.response["filecontent"]
return response
Depending on the value of the
Content-Disposition
header, the browser
receiving the response may behave differently. If unset, the value defaults to
"attachment"
.
If
frappe.response.display_content_as
is set to
"inline"
, it indicates
that the content is expected to be displayed inline in the browser, that is, as
a Web page or as part of a Web page, while
"attachment"
means the contents
are to be downloaded and saved locally.
To create an API endpoint that would directly download the file you require, you could craft something like the following to download the file directly.
@frappe.whitelist()
def download(name):
file = frappe.get_doc("File", name)
frappe.response.filename = file.file_name
frappe.response.filecontent = file.get_content()
frappe.response.type = "download"
frappe.response.display_content_as = "attachment"