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