mirror of
https://github.com/tenrok/axios.git
synced 2026-06-17 19:21:29 +03:00
fix(core): keep default validateStatus when request passes undefined (#10899)
Co-authored-by: Jason Saayman <jasonsaayman@gmail.com>
This commit is contained in:
@@ -9,3 +9,8 @@
|
|||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
|
||||||
- **Types:** Add the missing readonly `name: 'CanceledError'` declaration to CommonJS `CanceledError` typings to match the ESM declarations. (**#10922**)
|
- **Types:** Add the missing readonly `name: 'CanceledError'` declaration to CommonJS `CanceledError` typings to match the ESM declarations. (**#10922**)
|
||||||
|
- **Config Merge:** Added `transitional.validateStatusUndefinedResolves` (default `true`) so applications can opt into treating explicit `validateStatus: undefined` like an omitted option by setting it to `false`. `validateStatus: null` still accepts every response status. (**#10899**, closes **#6688**)
|
||||||
|
|
||||||
|
## Release Tracking
|
||||||
|
|
||||||
|
- ESM/CJS typings are updated for `transitional.validateStatusUndefinedResolves`; README/docs updates are tracked in `PRE_RELEASE_DOCS.md` for release preparation.
|
||||||
|
|||||||
@@ -37,3 +37,23 @@ axios.get('https://api.example.com/users', {
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **Notes:** Add a security page row linking to the request-config section and add a `sensitiveHeaders` request-config entry marked Node.js only.
|
- **Notes:** Add a security page row linking to the request-config section and add a `sensitiveHeaders` request-config entry marked Node.js only.
|
||||||
|
|
||||||
|
### validateStatus undefined transitional option
|
||||||
|
|
||||||
|
- **Change:** Document `transitional.validateStatusUndefinedResolves` for the `validateStatus: undefined` merge behavior.
|
||||||
|
- **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #10899, closes #6688.
|
||||||
|
- **Status:** Pending.
|
||||||
|
- **Docs targets:** README request config section; `docs/pages/advanced/request-config.md` `validateStatus` section and request config example; translated request-config docs after English docs are finalized.
|
||||||
|
- **Required content:** Explain that `validateStatus: undefined` keeps legacy behavior by default and resolves every response status because `transitional.validateStatusUndefinedResolves` defaults to `true`. Explain that setting `transitional.validateStatusUndefinedResolves` to `false` makes explicit `validateStatus: undefined` behave like the option was omitted, so axios uses the configured/default validator and rejects non-2xx responses by default. Mention that `validateStatus: null` still accepts every response status, and users who disable the transitional behavior should use `null` or `() => true` when they intentionally want all statuses to resolve.
|
||||||
|
- **Examples:** Include a short opt-in example.
|
||||||
|
|
||||||
|
```js
|
||||||
|
axios.get('/user/12345', {
|
||||||
|
validateStatus: undefined,
|
||||||
|
transitional: {
|
||||||
|
validateStatusUndefinedResolves: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Notes:** This is release-prep documentation only; do not update README or docs pages in the feature/fix PR.
|
||||||
|
|||||||
@@ -397,6 +397,7 @@ declare namespace axios {
|
|||||||
clarifyTimeoutError?: boolean;
|
clarifyTimeoutError?: boolean;
|
||||||
legacyInterceptorReqResOrdering?: boolean;
|
legacyInterceptorReqResOrdering?: boolean;
|
||||||
advertiseZstdAcceptEncoding?: boolean;
|
advertiseZstdAcceptEncoding?: boolean;
|
||||||
|
validateStatusUndefinedResolves?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GenericAbortSignal {
|
interface GenericAbortSignal {
|
||||||
|
|||||||
Vendored
+1
@@ -284,6 +284,7 @@ export interface TransitionalOptions {
|
|||||||
clarifyTimeoutError?: boolean;
|
clarifyTimeoutError?: boolean;
|
||||||
legacyInterceptorReqResOrdering?: boolean;
|
legacyInterceptorReqResOrdering?: boolean;
|
||||||
advertiseZstdAcceptEncoding?: boolean;
|
advertiseZstdAcceptEncoding?: boolean;
|
||||||
|
validateStatusUndefinedResolves?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GenericAbortSignal {
|
export interface GenericAbortSignal {
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class Axios {
|
|||||||
clarifyTimeoutError: validators.transitional(validators.boolean),
|
clarifyTimeoutError: validators.transitional(validators.boolean),
|
||||||
legacyInterceptorReqResOrdering: validators.transitional(validators.boolean),
|
legacyInterceptorReqResOrdering: validators.transitional(validators.boolean),
|
||||||
advertiseZstdAcceptEncoding: validators.transitional(validators.boolean),
|
advertiseZstdAcceptEncoding: validators.transitional(validators.boolean),
|
||||||
|
validateStatusUndefinedResolves: validators.transitional(validators.boolean),
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -68,6 +68,28 @@ export default function mergeConfig(config1, config2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMergedTransitionalOption(prop) {
|
||||||
|
const transitional2 = utils.hasOwnProp(config2, 'transitional') ? config2.transitional : undefined;
|
||||||
|
|
||||||
|
if (!utils.isUndefined(transitional2)) {
|
||||||
|
if (utils.isPlainObject(transitional2)) {
|
||||||
|
if (utils.hasOwnProp(transitional2, prop)) {
|
||||||
|
return transitional2[prop];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transitional1 = utils.hasOwnProp(config1, 'transitional') ? config1.transitional : undefined;
|
||||||
|
|
||||||
|
if (utils.isPlainObject(transitional1) && utils.hasOwnProp(transitional1, prop)) {
|
||||||
|
return transitional1[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
function mergeDirectKeys(a, b, prop) {
|
function mergeDirectKeys(a, b, prop) {
|
||||||
if (utils.hasOwnProp(config2, prop)) {
|
if (utils.hasOwnProp(config2, prop)) {
|
||||||
@@ -120,5 +142,17 @@ export default function mergeConfig(config1, config2) {
|
|||||||
(utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue);
|
(utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
utils.hasOwnProp(config2, 'validateStatus') &&
|
||||||
|
utils.isUndefined(config2.validateStatus) &&
|
||||||
|
getMergedTransitionalOption('validateStatusUndefinedResolves') === false
|
||||||
|
) {
|
||||||
|
if (utils.hasOwnProp(config1, 'validateStatus')) {
|
||||||
|
config.validateStatus = getMergedValue(undefined, config1.validateStatus);
|
||||||
|
} else {
|
||||||
|
delete config.validateStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ export default {
|
|||||||
clarifyTimeoutError: false,
|
clarifyTimeoutError: false,
|
||||||
legacyInterceptorReqResOrdering: true,
|
legacyInterceptorReqResOrdering: true,
|
||||||
advertiseZstdAcceptEncoding: false,
|
advertiseZstdAcceptEncoding: false,
|
||||||
|
validateStatusUndefinedResolves: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ describe('requests (vitest browser)', () => {
|
|||||||
await expect(promise).resolves.toBeDefined();
|
await expect(promise).resolves.toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve when validateStatus is undefined', async () => {
|
it('should resolve when validateStatus is undefined by default', async () => {
|
||||||
const { request, promise } = startRequest('/foo', {
|
const { request, promise } = startRequest('/foo', {
|
||||||
validateStatus: undefined,
|
validateStatus: undefined,
|
||||||
});
|
});
|
||||||
@@ -292,6 +292,23 @@ describe('requests (vitest browser)', () => {
|
|||||||
await expect(promise).resolves.toBeDefined();
|
await expect(promise).resolves.toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/axios/axios/issues/6688
|
||||||
|
it('should reject when validateStatus is undefined and the transitional option is disabled', async () => {
|
||||||
|
const { request, promise } = startRequest('/foo', {
|
||||||
|
validateStatus: undefined,
|
||||||
|
transitional: { validateStatusUndefinedResolves: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
request.respondWith({ status: 500 });
|
||||||
|
const reason = await promise.catch((error) => error);
|
||||||
|
|
||||||
|
expect(reason).toBeInstanceOf(Error);
|
||||||
|
expect(reason.message).toBe('Request failed with status code 500');
|
||||||
|
expect(reason.config.method).toBe('get');
|
||||||
|
expect(reason.config.url).toBe('/foo');
|
||||||
|
expect(reason.response.status).toBe(500);
|
||||||
|
});
|
||||||
|
|
||||||
// https://github.com/axios/axios/issues/378
|
// https://github.com/axios/axios/issues/378
|
||||||
it('should return JSON when rejecting', async () => {
|
it('should return JSON when rejecting', async () => {
|
||||||
const { request, promise } = startRequest(
|
const { request, promise } = startRequest(
|
||||||
|
|||||||
@@ -353,5 +353,25 @@ describe('core::mergeConfig', () => {
|
|||||||
expect(mergeConfig({ validateStatus: obj }, {}).validateStatus).toBe(obj);
|
expect(mergeConfig({ validateStatus: obj }, {}).validateStatus).toBe(obj);
|
||||||
expect(mergeConfig({ validateStatus: null }, {}).validateStatus).toBe(null);
|
expect(mergeConfig({ validateStatus: null }, {}).validateStatus).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps legacy undefined behavior by default', () => {
|
||||||
|
expect(mergeConfig(defaults, { validateStatus: undefined }).validateStatus).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps config1 value when the transitional option is disabled (issue #6688)', () => {
|
||||||
|
const validateStatus = () => false;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
mergeConfig(
|
||||||
|
{ validateStatus },
|
||||||
|
{
|
||||||
|
validateStatus: undefined,
|
||||||
|
transitional: { validateStatusUndefinedResolves: false },
|
||||||
|
}
|
||||||
|
).validateStatus
|
||||||
|
).toBe(validateStatus);
|
||||||
|
|
||||||
|
expect(mergeConfig(defaults, { validateStatus: null }).validateStatus).toBe(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user