request.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { GraphQLError } from "graphql";
  2. import { get as get_token } from "./reducers/session";
  3. export class HttpError extends Error {
  4. status: number;
  5. constructor(status: number, message?: string) {
  6. super(message ?? `HTTP ${status}`);
  7. this.name = "HttpError";
  8. this.status = status;
  9. }
  10. }
  11. export const upload = () => {
  12. return {
  13. Authorization: `Bearer ${get_token()}`,
  14. };
  15. };
  16. export const options = (method: string): RequestInit => {
  17. return {
  18. credentials: "include",
  19. headers: {
  20. Authorization: `Bearer ${get_token()}`,
  21. "Content-Type": "application/json; charset=utf-8",
  22. },
  23. mode: "cors",
  24. method,
  25. };
  26. };
  27. export const get = async <R>(path: string): Promise<R> => {
  28. const response = await fetch(path, options("GET"));
  29. if (!response.ok) {
  30. // 尝试读取后端返回的错误信息
  31. let message: string | undefined;
  32. try {
  33. const body = await response.json();
  34. message = body?.message ?? body?.error;
  35. } catch {
  36. // 忽略,body 可能不是 JSON
  37. }
  38. throw new HttpError(response.status, message);
  39. }
  40. const res: R = await response.json();
  41. return res;
  42. };
  43. export const delete_ = async <R>(path: string): Promise<R> => {
  44. const response = await fetch(path, options("DELETE"));
  45. const res: R = await response.json();
  46. return res;
  47. };
  48. export const delete_2 = async <Q, R>(path: string, body: Q): Promise<R> => {
  49. const data = options("DELETE");
  50. data.body = JSON.stringify(body);
  51. const response = await fetch(path, data);
  52. const res: R = await response.json();
  53. return res;
  54. };
  55. export interface IGraphqlResponse<R> {
  56. data?: R;
  57. errors?: GraphQLError[];
  58. }
  59. export const graphql = async <Q, R>(
  60. query: string,
  61. variables: Q
  62. ): Promise<IGraphqlResponse<R>> => {
  63. const res: IGraphqlResponse<R> = await post("/graphql", { query, variables });
  64. return res;
  65. };
  66. // https://github.github.io/fetch/#options
  67. export const post = async <Q, R>(path: string, body: Q): Promise<R> => {
  68. const data = options("POST");
  69. data.body = JSON.stringify(body);
  70. const response = await fetch(path, data);
  71. const res: R = await response.json();
  72. return res;
  73. };
  74. export const patch = <Request, Response>(
  75. path: string,
  76. body: Request
  77. ): Promise<Response> => {
  78. const data = options("PATCH");
  79. data.body = JSON.stringify(body);
  80. return fetch(path, data).then((res) => {
  81. if (res.status === 200) {
  82. return res.json();
  83. }
  84. throw res.text();
  85. });
  86. };
  87. export const put = <Request, Response>(
  88. path: string,
  89. body: Request
  90. ): Promise<Response> => {
  91. const data = options("PUT");
  92. data.body = JSON.stringify(body);
  93. return fetch(path, data).then((res) =>
  94. res.status === 200
  95. ? res.json()
  96. : res.json().then((err) => {
  97. throw err;
  98. })
  99. );
  100. };
  101. export const download = (path: string, name: string) => {
  102. const data = options("GET");
  103. fetch(path, data)
  104. .then((response) => response.blob())
  105. .then((blob) => {
  106. const url = window.URL.createObjectURL(blob);
  107. const a = document.createElement("a");
  108. a.href = url;
  109. a.download = name;
  110. document.body.appendChild(a); // for firefox
  111. a.click();
  112. a.remove();
  113. });
  114. };