Skip to content

Commit

Permalink
🐛 Prevent overriding content-type when passing body in http methods. #75
Browse files Browse the repository at this point in the history
  • Loading branch information
elbywan committed Apr 13, 2020
1 parent 76b32fd commit f6f9302
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 29 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,9 @@ wretch().post({ json: 'body' }, { credentials: "same-origin" })
wretch().json({ json: 'body'}).options({ credentials: "same-origin" }).post()
```
**NOTE:** For methods having a body argument if the value is an `Object` it is assumed that it is a JSON payload and apply the same behaviour
as calling `.json(body)`, unless the `Content-Type` header has been set to something else beforehand.
| [get](#getoptions) | [delete](#deleteoptions) | [put](#putbody-options) | [patch](#patchbody-options) | [post](#postbody-options) | [head](#headoptions) | [opts](#optsoptions) |
|-----|-----|-----|-----|-----|-----|-----|
Expand Down
2 changes: 1 addition & 1 deletion dist/bundle/wretch.esm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/bundle/wretch.esm.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/bundle/wretch.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/bundle/wretch.js.map

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions dist/wretcher.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,35 +124,35 @@ export declare class Wretcher {
/**
* Performs a get request.
*/
get(options?: any): ResponseChain & Promise<any>;
get(options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Performs a delete request.
*/
delete(options?: any): ResponseChain & Promise<any>;
delete(options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Performs a put request.
*/
put(body?: any, options?: any): ResponseChain & Promise<any>;
put(body?: any, options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Performs a post request.
*/
post(body?: any, options?: any): ResponseChain & Promise<any>;
post(body?: any, options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Performs a patch request.
*/
patch(body?: any, options?: any): ResponseChain & Promise<any>;
patch(body?: any, options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Performs a head request.
*/
head(options?: any): ResponseChain & Promise<any>;
head(options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Performs an options request
*/
opts(options?: any): ResponseChain & Promise<any>;
opts(options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Replay a request.
*/
replay(options?: any): ResponseChain & Promise<any>;
replay(options?: WretcherOptions): ResponseChain & Promise<any>;
/**
* Sets the request body with any content.
* @param contents The body contents
Expand Down
15 changes: 12 additions & 3 deletions dist/wretcher.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/wretcher.js.map

Large diffs are not rendered by default.

34 changes: 22 additions & 12 deletions src/wretcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export type WretcherOptions = RequestInit & {

export type DeferredCallback = (wretcher: Wretcher, url: string, options: WretcherOptions) => Wretcher

const JSON_MIME = "application/json"
const CONTENT_TYPE_HEADER = "Content-Type"

/**
* The Wretcher class used to perform easy fetch requests.
*
Expand Down Expand Up @@ -133,7 +136,7 @@ export class Wretcher {
* @param headerValue Header value
*/
content(headerValue: string) {
return this.headers({ "Content-Type" : headerValue })
return this.headers({ [CONTENT_TYPE_HEADER] : headerValue })
}

/**
Expand Down Expand Up @@ -189,10 +192,17 @@ export class Wretcher {
})
}

private method(method, options = {}, body = null) {
private method(method : string, options = {}, body = null) {
const headers = this._options.headers
let baseWretcher =
!body ? this :
typeof body === "object" ? this.json(body) :
typeof body === "object" && (
!headers ||
Object.entries(headers).every(([k, v]) =>
k.toLowerCase() !== CONTENT_TYPE_HEADER.toLowerCase() ||
v === JSON_MIME
)
) ? this.json(body) :
this.body(body)
baseWretcher = baseWretcher.options({ ...options, method })
const deferredWretcher = baseWretcher._deferredChain.reduce((acc: Wretcher, curr) => curr(acc, acc._url, acc._options), baseWretcher)
Expand All @@ -202,49 +212,49 @@ export class Wretcher {
/**
* Performs a get request.
*/
get(options?) {
get(options? : WretcherOptions) {
return this.method("GET", options)
}
/**
* Performs a delete request.
*/
delete(options?) {
delete(options? : WretcherOptions) {
return this.method("DELETE", options)
}
/**
* Performs a put request.
*/
put(body?, options?) {
put(body? : any, options? : WretcherOptions) {
return this.method("PUT", options, body)
}
/**
* Performs a post request.
*/
post(body?, options?) {
post(body? : any, options? : WretcherOptions) {
return this.method("POST", options, body)
}
/**
* Performs a patch request.
*/
patch(body?, options?) {
patch(body? : any, options? : WretcherOptions) {
return this.method("PATCH", options, body)
}
/**
* Performs a head request.
*/
head(options?) {
head(options? : WretcherOptions) {
return this.method("HEAD", options)
}
/**
* Performs an options request
*/
opts(options?) {
opts(options? : WretcherOptions) {
return this.method("OPTIONS", options)
}
/**
* Replay a request.
*/
replay(options?) {
replay(options? : WretcherOptions) {
return this.method(this._options.method, options)
}

Expand All @@ -260,7 +270,7 @@ export class Wretcher {
* @param jsObject An object which will be serialized into a JSON
*/
json(jsObject: object) {
return this.content("application/json").body(JSON.stringify(jsObject))
return this.content(JSON_MIME).body(JSON.stringify(jsObject))
}
/**
* Converts the javascript object to a FormData and sets the request body.
Expand Down
8 changes: 8 additions & 0 deletions test/browser/spec/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ describe("Wretch", function() {
expect(roundTrip).toBe(reference)
roundTrip = await wretch(`${_URL}/urlencoded/roundTrip`).formUrl(jsonObject).post().text()
expect(roundTrip).toEqual(reference)
// Ensure that calling .json with the shorthand works
const roundTrip3 = await wretch(`${_URL}/json/roundTrip`).json({}).post(jsonObject).json()
expect(roundTrip3).toEqual(jsonObject)
// Ensure that it preserves any content type set previously
try {
await wretch(`${_URL}/json/roundTrip`).content("bad/content").post(jsonObject).json()
fail("should have thrown")
} catch(e) {}
})

it("should send a FormData object", async function() {
Expand Down
10 changes: 9 additions & 1 deletion test/node/wretch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe("Wretch", function () {

expect(() => wretch("...").query({ a: 1, b: 2 })).toThrow("URLSearchParams is not defined")
expect(() => wretch("...").formData({ a: 1, b: 2 })).toThrow("FormData is not defined")
expect(() => wretch("...").get("...")).toThrow("fetch is not defined")
expect(() => wretch("...").get()).toThrow("fetch is not defined")

wretch().polyfills({
fetch: fetchPolyfill(),
Expand Down Expand Up @@ -120,6 +120,14 @@ describe("Wretch", function () {
// Using shorthand
const roundTrip2 = await wretch(`${_URL}/json/roundTrip`).post(jsonObject).json()
expect(roundTrip2).toEqual(jsonObject)
// Ensure that calling .json with the shorthand works
const roundTrip3 = await wretch(`${_URL}/json/roundTrip`).json({}).post(jsonObject).json()
expect(roundTrip3).toEqual(jsonObject)
// Ensure that it preserves any content type set previously
try {
await wretch(`${_URL}/json/roundTrip`).content("bad/content").post(jsonObject).json()
fail("should have thrown")
} catch(e) {}
})

it("should perform an url encoded form data round trip", async function () {
Expand Down

0 comments on commit f6f9302

Please sign in to comment.