onError
ν¨μλ λ¬Έμμ λ²κ·Έκ° μλ κ² κ°μ΅λλ€. μ΄ λ¬Έμ λ #698μμ μ΄λ―Έ μΈκΈλμμ§λ§ ν΄κ²°μ μν΄ μ‘°κΈ λ ꡬ체νν΄μΌ νλ€κ³ μκ°νμ΅λλ€.
apollo-boost
μμ λλ apollo-link-error
μμ μ§μ onError
λ₯Ό μ¬μ©νμ¬ μ€λ₯λ₯Ό κ°λ‘μ± λ λ¬Έμ μλ μ€λ₯ κ°μ²΄μ λ€μμ μννλ λ° μ¬μ©ν μ μλ response
κ°μ²΄κ° ν¬ν¨λμ΄ μλ€κ³ λμ μμ΅λλ€. μ€λ₯λ₯Ό 무μν©λλ€.
μ΄κ²μ μλλ λμμΌ μ μκΈ° λλ¬Έμ λ¬Έμ νμΈλμ νμνμ§λ§ λ¬Έμμμ networkError
μΉμ
μ νμ λ¨λ½μμ response
κ°μ²΄λ₯Ό μΈκΈνλ―λ‘ λ€μκ³Ό κ°μ κ²½μ° μ½κ° λͺ
νν ν΄μΌ ν©λλ€. κ²½μ°.
μμλλ λμ
response.errors = null;
λ₯Ό μ€μ νλ©΄ λ€λ₯Έ μ€λ₯κ° λ°μνμ§ μκ³ μ€λ₯κ° μ νλλ κ²μ λ°©μ§ν μ μμ΅λλ€.
μ€μ νλ
networkError
λ₯Ό μ‘μ λ response
νλκ° μ μλμ§ μμ response.errors
λ₯Ό μ€μ νλ €κ³ ν λ ꡬ문 μ€λ₯κ° λ°μν©λλ€. νμ¬λ‘μλ λΆκ°λ₯ν μ΄μ λ‘ μ΄κ²μ μ€μ λ‘ λ€νΈμν¬ μ€λ₯κ° μ νλλ κ²μ λ°©μ§νμ§λ§ λμ λ€λ₯Έ μ€λ₯λ₯Ό λ°μμν΅λλ€.
λ΄ μ€μ (MacOS 10.14.1 w. Chrome 70.0.3538.77) response.errors = null
λ₯Ό μ€μ νλ©΄ Uncaught TypeError: Cannot set property 'errors' of undefined
κ° λ°μνκ³ response = { errors: null }
λ Uncaught Error: "response" is read-only
κ° λ°μν©λλ€.
_λ¨μν_ μ¬μμ°
λ€μμ apollo-boost
λ₯Ό μ¬μ©ν μ¬μμ°μ
λλ€. apollo-link-error
λ₯Ό μ§μ μ¬μ©νμ¬λ κ°μ λ¬Έμ κ° λ°μν©λλ€.
import ApolloClient from 'apollo-boost';
const client = ApolloClient({
uri: '/my_api',
onError: (({ response, networkError }) => {
if (networkError && networkError.statusCode === 401) {
console.log(response); // prints 'undefined'
response.errors = null; // will throw 'undefined' error
response = { errors: null }; // will throw a 'read-only' error
}
})
});
μ΄μ λΌλ²¨
μ΄ λ²κ·Έμ λν μμ ν΄κ²° λ°©λ²μ΄ μμ΅λκΉ?
+1 μλ² νκ²½μμ apollo ν΄λΌμ΄μΈνΈλ₯Ό μ¬μ©νλ κ²½μ° λλ²κΉ μ λν κ½€ ν° λ¬Έμ μ λλ€(리쑸λ²μ λν΄ λ€λ₯Έ GraphQL μλ²μμ λ°μ΄ν° μ½κΈ°).
+1
쑰건λΆλ‘ μ€λ₯λ₯Ό 무μνλ ν΄κ²° λ°©λ²μ΄ μμ΅λκΉ?
λλ λνμ΄ λ¬Έμ κ° μμ΅λλ€.
λμκ² ν° λ¬Έμ λ μ΄κ²μ΄ UIκ° μ΄ μ€λ₯λ₯Ό μΈμνμ§ λͺ»νκ² νλ€λ κ²μ λλ€. μ€μ λ‘ ν° μ€λ₯κ° λ°μνλ©΄ λ‘λ μνλ‘ μ μ§λ©λλ€.
λλ μ΄κ²μ μ‘°μ¬νκ³ λ¬Έμμ λ°λ₯΄λ©΄ μ½λ°±μ μλ΅ν΄μΌν©λλ€.
https://www.apollographql.com/docs/link/links/error.html#callback
μΌλ° μ½λ°±μ μ¬μ©νλ©΄ λ€μκ³Ό κ°μ μΌμ΄ λ°μν©λλ€ .
κ·Έλ¬λ networkError: https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-error/src/index.ts#L62
λ³ν©λ μκΈ°μ λν ν΅μ°°λ ₯μ΄ μμ΅λκΉ? :)
μλ§λ 곧, apollo νμ μλ‘μ΄ ν΄λΌμ΄μΈνΈμ λ°μ λ²μ μ μΆμνλ λ° μ§μ€ν κ²μ λλ€.
μ΄ λ¬Έμ μ μ§μ μ΄ μμ΅λκΉ?
μ΄μ λν μ§μ μ΄ μλμ§λ κΆκΈν©λλ€. μλλ©΄ κ·Έλμμ ν΄κ²° λ°©λ²? APIμμ UIλ‘ μ¬μ©μ μ μ μ€λ₯ λ©μμ§λ₯Ό μ ννλ λ°©λ²μ κ°κ³ μΆμ΅λλ€.
λλ λν κ°μ λ¬Έμ μ μ§λ©΄νμ΅λλ€. λλ₯Ό μν ν΄κ²° λ°©λ²μ λΉ Observableμ λ°ννλ κ²μ΄μμ΅λλ€.
μμΈλ₯Ό λμ§μ§ μκ³ μλ²κ° 503μ μλ΅νλ κ²½μ°λ₯Ό λλΉνμ¬ μ‘°μ©ν μ μ§ νμ΄μ§λ‘ 리λλ μ νκ³ μΆμμ΅λλ€. μ¬κΈ° λ΄ μ½λκ° μμ΅λλ€. μλ§λ λκ΅°κ°μκ² λμμ΄ λ κ²μ λλ€.
import { Observable } from 'apollo-link';
...
onError(({ networkError }: any) => {
if (networkError && networkError.status === 503) {
this.redirectService.goToMaintenance();
return Observable.of();
}
});
...
@ luki215 μ€λ₯ μ½λ νλκ° μμ΅λλ€
@Sceat μ΄λ€ apollo-link
μ μ¬μ©ν©λκΉ? λλ apollo-angular-link-http
λ₯Ό μ¬μ©νλ―λ‘ μ°¨μ΄κ° μμ μ μμ΅λλ€.
@luki215 μ€μ λ‘ λ΄ μλͺ»μ΄μμ΅λλ€. AWS api κ²μ΄νΈμ¨μ΄λ₯Ό μ¬μ©νμ¬ 200
μ½λκ° μλ λ€λ₯Έ νλͺ©μ λν΄ CORS μλ΅μ μ¬λ°λ₯΄κ² ꡬμ±νμ§ μμμ΅λλ€.
λ¬Έμ κ° λ°μνλ μ¬λλ€μ κ²½μ° κΈ°λ³Έ 4xxμμ API κ²μ΄νΈμ¨μ΄ μλ΅μ μΆκ°ν μ μμ΅λλ€.
λλ serverless.ymlμμ
yml
GatewayDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
λλ κ°μ κ±Έλ¦Όλμ λΆλͺνκ³ μμΌλ©° μ€λ₯ λ©μμ§λ₯Ό μμ±νκΈ° μν΄ λ΄ λ°μ μμ© νλ‘κ·Έλ¨μ λ©μμ§λ₯Ό μ λ¬ν μ μλ€κ³ μκ°ν©λλ€. μ΄ λ¬Έμ μ λν μμ μ¬νμ΄ μ§ν μ€μ λκΉ?
@strass λ μ΄ μλ΅ νλμ λν΄ λͺ¨λ₯΄μ§λ§ μ€λ₯λ₯Ό μ²λ¦¬νκΈ° μν΄ λ€νΈμν¬ μ€λ₯λ₯Ό μΌ€ μ μμ΅λλ€.
export default onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
for (let { extensions: { code } } of graphQLErrors)
handleTheError(code)
}
if (networkError) {
switch (networkError.statusCode) {
case 500:
//
break
case 429:
//
break
case undefined:
//
break
default:
console.error(networkError)
break
}
}
})
λΉ Observableμ λ°ννλ @luki215 μ ν΄κ²° λ°©λ²μ μλνμ§λ§ μ κ²½μ°μλ μ΄μ react-apolloκ° μμΈλ₯Ό throwνκ² λ§λ€ κ²μ λλ€.
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
at Mutation._this.onMutationCompleted (react-apollo.esm.js:627)
at react-apollo.esm.js:564
Mutation
λ₯Ό μ¬μ©νμ¬ κ΅¬μ± μμμ λ°νλ μ€λ₯λ₯Ό λ³κ²½νκΈ° μν΄ μ΄κ²μ μ¬μ©νκ³ μμ΅λλ€.
const errorLink = onError(({ forward, networkError, operation }) => {
if (networkError) {
const { message: errorMessage } = networkError.result;
switch (networkError.statusCode) {
case 400:
if (errorMessage) {
networkError.message = errorMessage;
}
break;
case 403:
window.location = '/users/login';
break;
default:
break;
}
}
forward(operation);
});
μ¬λλ€μ΄ μ¬κΈ°μ μ°Ύκ³ μλ ν΅μ¬μ networkError
μμ λ°νλ μ€λ₯ λ¬Έμμ΄μ μ¬μ μνλ κΈ°λ₯μ΄λ©° μ΄ networkError.message = 'Some custom error message.'
λ‘ μλ£λ©λλ€.
apollo-link-rest
λ° μ΄ ν¨ν€μ§λ₯Ό νν€μΉ ν ν΄ν€ ν΄κ²° λ°©λ²μ μκ°ν΄ λμ΅λλ€. onError
λ₯Ό μ¬μ©νλ λμ λ΄ μμ μ λ€μ μμ±νμ΅λλ€(μλ).
μλ: NetworkError
λ₯Ό κ°λ‘μ± μ‘°κ±΄λΆλ‘ GraphQLError
λ‘ λ³νν©λλ€. κ·Έ μ΄μ λ GraphQL κ·μΉμ λ°λ₯΄μ§ μκ³ 400 μν μ½λμ ν¨κ» {"message": "{ \"key\": \"not valid\"}"}
μ κ°μ λͺ¨μμ λ°ννλ REST λμ μ μ¬μ©νκ³ μκΈ° λλ¬Έμ
λλ€. μ΄κ²μ NetworkErrorλ‘ μ·¨κΈλμ΄μλ μλ©λλ€.
μλ μ리: μ΄ μ루μ
μ mutate
promise catch
λ΄μμ κ²μ λ° κ΅¬λ¬Έ λΆμνκ³ νμ μμμ μνλ₯Ό μμ ν μ μλ λ°©μμΌλ‘ μλ΅μμ μ€μ μ€λ₯ λ©μμ§λ₯Ό μ²΄μΈ μλλ‘ μ λ¬ν©λλ€. .
μλνμ§ μλ κ²:
observer.next({ data: { ... }, errors: { ... } })
λ₯Ό μ¬μ©νλ κ²μ μ νΈνμ§λ§ μλνμ§ μμ΅λλ€(ν΄λΉ λ°μ΄ν°λ μ무λ°λ κ°κ³ λμ°λ³μ΄ μ½μμ ν΄κ²°λμ§ μλ κ²μΌλ‘ 보μ
λλ€).observer.next()
λ° observer.error()
λ₯Ό μμλλ‘ νΈμΆνλ©΄ μμνλ λμ μ΄ μμ΅λλ€. μ€λ₯ νΈμΆλ§ μ μ©λλ κ² κ°μ΅λλ€.const errorLink = new ApolloLink((operation, forward) => {
return new Observable(observer => {
let sub: ZenObservable.Subscription;
try {
sub = forward!(operation).subscribe({
next: result => {
console.log("A proper result looks like:", result);
observer.next(result)
},
error: networkError => {
try {
const { message } = networkError.result;
const error = new GraphQLError(message);
const apolloError = new ApolloError({
errorMessage: "Some error happened",
graphQLErrors: [error],
networkError: undefined,
});
observer.error(new Error(message));
} catch (_) {
observer.error(networkError)
}
},
complete: () => {
console.log("Calling complete()");
observer.complete();
},
});
} catch (e) {
console.log("ErrorLink had an error of its own")
observer.error(e);
}
return () => {
if (sub) sub.unsubscribe();
};
});
});
μ΄κ²μ΄ μλνλ κ²μ²λΌ 보μ΄μ§λ§ mutate()
ν¨μλ ν΄κ²°λκ±°λ κ±°λΆλμ§ μλ κ² κ°μ΅λλ€ then()
λλ catch()
λͺ¨λ νΈμΆλμ§ μμ΅λλ€.
const errorLink = new ApolloLink((operation, forward) => {
return new Observable(observer => {
let sub: ZenObservable.Subscription;
try {
sub = forward!(operation).subscribe({
next: result => {
console.log("A proper result looks like:", result);
observer.next(result)
},
error: networkError => {
try {
const { message } = networkError.result;
const error = new GraphQLError(message);
// this is called, but it seems to be the end of the line
console.log("Intercepting error, returning success");
observer.next({ data: {}, errors: [ error ]});
} catch (localError) {
console.error("Error in link:", localError);
observer.error(networkError)
}
},
complete: () => {
console.log("Calling complete()");
observer.complete();
},
});
} catch (e) {
console.log("ErrorLink had an error of its own")
observer.error(e);
}
return () => {
if (sub) sub.unsubscribe();
};
});
});
κ·Έλ₯ λ°κ³ ...
μ§κΈ κ°μ μ€λ₯μ κ±Έλ € λμ΄μ‘μ΅λλ€.
κ΅¬μ± μμ μ체μ μ€λ₯λ₯Ό μ²λ¦¬νκΈ° μν΄ μλ΅ νλμ μ€λ₯λ₯Ό μ¬μ€μ νκ³ μΆμ΅λλ€...
μ΄λ€ μ§μ μ΄ μμ΅λκΉ?
κ°μ λ¬Έμ μ +1.
(μ μ΄λ λ΄ μ€λ₯ μ ν λ¬Έμ μ λν) ν΄κ²°μ± μ μ°Ύμμ μλ μλ€κ³ μκ°ν©λλ€.
errorLink μμ map
λ₯Ό forEach
λ‘ λ°κΎΈλ©΄ μ€λ₯κ° UIμ μ€λ₯λ₯Ό μ νν μ μμμ μλ―Έν©λλ€(μ΄μ μλ UI κ΅¬μ± μμμ result.errorκ° μ μλμ§ μμμΌλ μ΄μ λ μ€λ₯κ° μμ).
const link = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
- graphQLErrors.map(({ message, locations, path }) =>
+ graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
λλ λν κ°μ λ¬Έμ μ μ§λ©΄νμ΅λλ€. λλ₯Ό μν ν΄κ²° λ°©λ²μ λΉ Observableμ λ°ννλ κ²μ΄μμ΅λλ€.
μμΈλ₯Ό λμ§μ§ μκ³ μλ²κ° 503μ μλ΅νλ κ²½μ°λ₯Ό λλΉνμ¬ μ‘°μ©ν μ μ§ νμ΄μ§λ‘ 리λλ μ νκ³ μΆμμ΅λλ€. μ¬κΈ° λ΄ μ½λκ° μμ΅λλ€. μλ§λ λκ΅°κ°μκ² λμμ΄ λ κ²μ λλ€.
import { Observable } from 'apollo-link'; ... onError(({ networkError }: any) => { if (networkError && networkError.status === 503) { this.redirectService.goToMaintenance(); return Observable.of(); } }); ...
μ΄κ²μ μ μκ² ν¨κ³Όκ° μμμ§λ§ λ¬Έμμμ λ§νλ κ²κ³Ό μ νν λ°λμ λλ€. μλ₯Ό λ€μ΄ "μ€λ₯ μ½λ°±μ μμ²μ λ€μ μλνλ €λ κ²½μ° forward(operation) νΈμΆμμ κ΄μ°° κ°λ₯ν νλͺ©μ μ νμ μΌλ‘ λ°νν μ μμ΅λλ€. λ€λ₯Έ κ²μ λ°νν΄μλ μ λ©λλ€."
μ€λ Apolloλ₯Ό μ λ°μ΄νΈν ν μμ μ루μ μ λ μ΄μ apollo-hooksλ₯Ό ν΅ν΄ UIμ λν graphQL μ€λ₯λ₯Ό μ λ¬νμ§ μμ΅λλ€.
λꡬλ μ§ λ€λ₯Έ ν΄κ²°μ± μ΄ μμ΅λκΉ?
@strass μμ μ루μ
μ΄ λ¬Έμ λ₯Ό ν΄κ²°νλ μ΄μ λ₯Ό μ€λͺ
ν μ μμ΅λλ€. IMO κ·Έλ€μ forEach
λ° map
λ₯Ό μ¬μ©νμ¬ μ νν λμΌν μμ
μ μνν©λλ€.
λ μ΄μ κ·Έλ μ§ μμ΅λλ€(https://github.com/apollographql/apollo-link/issues/855#issuecomment-538010335 μ°Έμ‘°).
μ΄μ λν μ§μ μ΄ μμ΅λκΉ?
μλ΅ κ°μ²΄λ₯Ό μ ν μ»μ μ μλ€λ©΄ μλ²κ° μ¬μΈμ¦μ μν΄ "Location" ν€λκ° μλ 302λ₯Ό λ°ννλ κ²½μ°λ₯Ό μ΄λ»κ² μ²λ¦¬ν΄μΌ ν κΉμ?
λκ΅°κ° λ¨μκ° μμΌλ©΄ μλ €μ£Όμμμ€. μλνλ©΄ μ°λ¦¬λ μν΄λ‘λ₯Ό ν¬κΈ°ν μκ°μ νκ³ μκΈ° λλ¬Έμ λλ€.
μ΄κ²μ https://github.com/apollographql/apollo-link/issues/297#issuecomment -350488527μ ν° λμμ΄ λμμΌλ©° λ€νΈμν¬ μλ΅μ μ½μ μ μκ² ν΄μ€λλ€.
μΈμ¦ κ³μΈ΅/κ²μ΄νΈμ¨μ΄ APIμμ λ¬΄λ¨ μλ΅μ΄ λ°νλ λ μ€μν κ²μ λλ€.
μ΄κ²μ ν΅μ¬ κΈ°λ₯μ μΌλΆλ‘ λ³΄κ³ μΆμ΅λλ€!
μ΄μ λν μ§μ μ΄ μμ΅λκΉ? μ΄κ²μ μλλ λμμ λλ€. 200μ΄ μλ μν μ½λλ‘ GraphQL μ€λ₯λ₯Ό ν΄λΌμ΄μΈνΈμ μ λ¬ν μ μλ λ€λ₯Έ λ°©λ²μ 무μμ λκΉ?
λ€νΈμν¬ μ€λ₯λ₯Ό ν¬μ°©νκ³ μμ νλ λ° λ¬Έμ κ° μλ λ€λ₯Έ μ¬λμ μν μ°Έκ³ μ¬νμ λλ€. λλ graphqlμ λ΄λΆ μ€λ₯λ‘ λ€νΈμν¬ μ€λ₯λ₯Ό νλ €κ³ μλνκ³ λ€μκ³Ό κ°μ΄ λλ¬μ΅λλ€.
const errorHandler = onError(({ graphQLErrors, networkError, operation, forward, response }) => {
if (graphQLErrors) {
console.error('apollo errors', graphQLErrors);
}
if (networkError) {
console.error('apollo network errors', networkError);
if (!!networkError['error'] && !!networkError['error']['errors'] && networkError['error']['errors'][0]) {
console.error('unwrapping apollo network errors');
networkError.message = networkError['error']['errors'][0].message;
// you may also be able to set networkError.message to null based on criteria to remove the error, even if you can't prevent an error from being triggered altogether
}
}
}
);
// Create an http link:
const httpLink = new HttpLink(this.httpClient).create({
uri: this.uri,
headers: new HttpHeaders({
Authorization: this.token
}),
includeExtensions: true
});
const httpErrorLink = ApolloLink.from([
errorHandler,
httpLink,
]);
κΈ°λ³Έμ μΌλ‘ μΌλ° λ€νΈμν¬ μ€λ₯ λ©μμ§μΈ λ©μμ§λ₯Ό 첫 λ²μ§Έ λ΄λΆ μ€λ₯ λ©μμ§λ‘ λ°κΏλλ€. λμ²λΌ μ¬κΈ°κΉμ§ μ¨ μ¬λλ€μκ² λμμ΄ λ μ μλ€.
κ°μ₯ μ μ©ν λκΈ
μ΄ λ¬Έμ μ μ§μ μ΄ μμ΅λκΉ?