媒体类型处理程序#
媒体类型处理程序允许PostgREST提供自定义媒体类型。这些处理程序扩展了 builtin ones 并且还可以覆盖它们。
媒体类型使用类型别名表示 domains 他们的名字必须符合 RFC 6838 requirements 。
CREATE DOMAIN "application/json" AS json;
使用这些域, functions 可以成为操纵者和 user-defined aggregates 可以充当的处理程序 表和视图 和 表值函数 。
重要
PostgREST供应商介质类型 (
application/vnd.pgrst.plan
,application/vnd.pgrst.object
和application/vnd.pgrst.array
)不能被覆盖。长媒体类型,如
application/vnd.openxmlformats-officedocument.wordprocessingml.document
无法表示为属性域,因为它们超过 PostgreSQL identifier length 。对于这些,您可以使用 “任何”处理程序 。
处理程序函数#
作为一个示例,让我们获得 TWKB PostGIS几何图形的压缩二进制格式。
create extension postgis;
create table lines (
id int primary key
, name text
, geom geometry(LINESTRING, 4326)
);
insert into lines values (1, 'line-1', 'LINESTRING(1 1,5 5)'::geometry), (2, 'line-2', 'LINESTRING(2 2,6 6)'::geometry);
为此,您可以创建供应商媒体类型。
create domain "application/vnd.twkb" as bytea;
并将其用作函数的返回类型,以使其成为处理程序。
create or replace function get_line (id int)
returns "application/vnd.twkb" as $$
select st_astwkb(geom) from lines where id = get_line.id;
$$ language sql;
备注
对于PostgreSQL<=12,您需要对函数体进行强制转换 st_astwkb(geom)::"application/vnd.twkb"
。
现在您可以请求 TWKB
输出如下:
curl 'localhost:3000/rpc/get_line?id=1' -i \
-H "Accept: application/vnd.twkb"
HTTP/1.1 200 OK
Content-Type: application/vnd.twkb
# binary output
请注意,PostgREST将自动设置 Content-Type
至 application/vnd.twkb
。
表/视图的处理程序#
要从压缩格式中获益,如 TWKB
,则获取多行比获取一行更有意义。让我们通过为表添加一个处理程序来实现这一点。
通过使用域媒体类型作为其转换或最终函数的返回类型,可以将用户定义的聚合转换为处理程序。
让我们为这个例子创建一个转移函数。
create or replace function twkb_handler_transition (state bytea, next lines)
returns "application/vnd.twkb" as $$
select state || st_astwkb(next.geom);
$$ language sql;
现在,我们将在为 lines
桌子。
create or replace aggregate twkb_agg (lines) (
initcond = ''
, stype = "application/vnd.twkb"
, sfunc = twkb_handler_transition
);
备注
您可以通过测试看到此聚合与以下各项一起工作:
SELECT twkb_agg(l) from lines l;
twkb_agg
---------------------------------------------------------------
\xa20002c09a0cc09a0c80ea3080ea30a2000280b51880b51880ea3080ea30
(1 row)
现在,您可以使用 twkb
媒体类型:
curl 'localhost:3000/lines' -i \
-H "Accept: application/vnd.twkb"
HTTP/1.1 200 OK
Content-Type: application/vnd.twkb
# binary output
如果有一个表值函数返回相同的表类型,则处理程序也可以对其执行操作。
create or replace function get_lines ()
returns setof lines as $$
select * from lines;
$$ language sql;
curl 'localhost:3000/get_lines' -i \
-H "Accept: application/vnd.twkb"
HTTP/1.1 200 OK
Content-Type: application/vnd.twkb
# binary output
重写内置处理程序#
让我们重写现有的 text/csv
表的处理程序,以提供更复杂的CSV输出。它将包括一个 Byte order mark (BOM) 外加一个 Content-Disposition
标头以设置下载文件的名称。
为标准创建属性域 text/csv
媒体类型。
create domain "text/csv" as text;
以及返回域的转换函数。
create or replace function bom_csv_trans (state text, next lines)
returns "text/csv" as $$
select state || next.id::text || ',' || next.name || ',' || next.geom::text || E'\n';
$$ language sql;
这一次,我们将添加最后一个函数。这将添加CSV标头、BOM和 Content-Disposition
标题。
create or replace function bom_csv_final (data "text/csv")
returns "text/csv" as $$
-- set the Content-Disposition header
select set_config('response.headers', '[{"Content-Disposition": "attachment; filename=\"lines.csv\""}]', true);
select
-- EFBBBF is the BOM in UTF8 https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
convert_from (decode (E'EFBBBF', 'hex'),'UTF8') ||
-- the header for the CSV
(E'id,name,geom\n' || data);
$$ language sql;
现在,将转换和最终函数用作新聚合的一部分。
create or replace aggregate bom_csv_agg (lines) (
initcond = ''
, stype = "text/csv"
, sfunc = bom_csv_trans
, finalfunc = bom_csv_final
);
备注
您可以使用以下命令进行测试:
select bom_csv_agg(l) from lines l;
bom_csv_agg
-----------------------------------------------------------------------------------------------------
id,name,geom +
1,line-1,0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440+
2,line-2,0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840+
(1 row)
并按如下方式请求:
curl 'localhost:3000/lines' -i \
-H "Accept: text/csv"
HTTP/1.1 200 OK
Content-Type: text/csv
Content-Disposition: attachment; filename="lines.csv"
id,name,geom
1,line-1,0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440
2,line-2,0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840
“任何”处理程序#
为了获得更大的灵活性,还可以使用名为的域来定义一个通用处理程序 */*
(任何媒体类型)。此处理程序遵循以下规则:
它响应所有媒体类型,甚至响应不包含
Accept
标题。它设置了
Content-Type
标题收件人application/octet-stream
默认情况下,但可以在函数内部使用 响应标头 。它会覆盖所有其他处理程序 (builtin 或定制),所以最好是为独立的函数或视图执行此操作。
让我们为一个视图定义一个Any处理程序,该处理程序总是以 XML
输出。它会接受 text/xml
, application/xml
, */*
并拒绝其他媒体类型。
create domain "*/*" as bytea;
-- we'll use an .xml suffix for the view to be clear its output is always XML
create view "lines.xml" as
select * from lines;
-- transition function
create or replace function lines_xml_trans (state "*/*", next "lines.xml")
returns "*/*" as $$
select state || xmlelement(name line, xmlattributes(next.id as id, next.name as name), next.geom)::text::bytea || E'\n' ;
$$ language sql;
-- final function
create or replace function lines_xml_final (data "*/*")
returns "*/*" as $$
declare
-- get the Accept header
req_accept text := current_setting('request.headers', true)::json->>'accept';
begin
-- when we need to override the default Content-Type (application/octet-stream) set by PostgREST
if req_accept = '*/*' then
perform set_config('response.headers', json_build_array(json_build_object('Content-Type', 'text/xml'))::text, true);
elsif req_accept IN ('application/xml', 'text/xml') then
perform set_config('response.headers', json_build_array(json_build_object('Content-Type', req_accept))::text, true);
else
-- we'll reject other non XML media types, we need to reject manually since */* will command PostgREST to accept all media types
raise sqlstate 'PT415' using message = 'Unsupported Media Type';
end if;
return data;
end; $$ language plpgsql;
-- new aggregate
create or replace aggregate lines_xml_agg ("lines.xml") (
stype = "*/*"
, sfunc = lines_xml_trans
, finalfunc = lines_xml_final
);
在SQL上进行测试:
select (encode(lines_xml_agg(x), 'escape'))::xml from "lines.xml" x;
encode
------------------------------------------------------------------------------------------------------------------------------
<line id="1" name="line-1">0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440</line>+
<line id="2" name="line-2">0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840</line>+
现在我们可以省略 Accept
标头,它将使用XML进行响应。
curl 'localhost:3000/lines.xml' -i
HTTP/1.1 200 OK
Content-Type: text/xml
<line id="1" name="line-1">0102000020E610000002000000000000000000F03F000000000000F03F00000000000014400000000000001440</line>
<line id="2" name="line-2">0102000020E6100000020000000000000000000040000000000000004000000000000018400000000000001840</line>
并且它将只接受XML媒体类型。
curl 'localhost:3000/lines.xml' -i \
-H "Accept: text/xml"
HTTP/1.1 200 OK
Content-Type: text/xml
curl 'localhost:3000/lines.xml' -i \
-H "Accept: application/xml"
HTTP/1.1 200 OK
Content-Type: text/xml
curl 'localhost:3000/lines.xml' -i \
-H "Accept: unknown/media"
HTTP/1.1 415 Unsupported Media Type