mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-01 06:51:32 +00:00
Add configs to choose between ecoding parameters in the URL or request body (#1525)
* Allow extensions to set request bodies for GET and DELETE Even though the semantics of request bodies for GET and DELETE are undefined, it is legal to set request bodies for them. By default, GET and DELETE form parameters should be encoded as URLs. If, however, an extension defines `encodeParameters`, that should override the default and set the request bodies for GET and DELETE methods, just like it does for all the other HTTP methods. * Don't use URL params by default with DELETE * Re-enable skipped DELETE test * Remove reference to DELETE in comment * Add "useUrlParams" to requestConfig * Add config.methodsThatUseUrlParams * Add methodsThatUseUrlParams config tests * Don't switch to body params based on extension
This commit is contained in:
parent
18aa2470a0
commit
febfa1c6a7
32
src/htmx.js
32
src/htmx.js
@ -72,6 +72,7 @@ return (function () {
|
||||
defaultFocusScroll: false,
|
||||
getCacheBusterParam: false,
|
||||
globalViewTransitions: false,
|
||||
methodsThatUseUrlParams: ["get"],
|
||||
},
|
||||
parseInterval:parseInterval,
|
||||
_:internalEval,
|
||||
@ -2971,8 +2972,12 @@ return (function () {
|
||||
var requestAttrValues = getValuesForElement(elt, 'hx-request');
|
||||
|
||||
var eltIsBoosted = getInternalData(elt).boosted;
|
||||
|
||||
var useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0
|
||||
|
||||
var requestConfig = {
|
||||
boosted: eltIsBoosted,
|
||||
useUrlParams: useUrlParams,
|
||||
parameters: filteredParameters,
|
||||
unfilteredParameters: allParameters,
|
||||
headers:headers,
|
||||
@ -2997,6 +3002,7 @@ return (function () {
|
||||
headers = requestConfig.headers;
|
||||
filteredParameters = requestConfig.parameters;
|
||||
errors = requestConfig.errors;
|
||||
useUrlParams = requestConfig.useUrlParams;
|
||||
|
||||
if(errors && errors.length > 0){
|
||||
triggerEvent(elt, 'htmx:validation:halted', requestConfig)
|
||||
@ -3008,26 +3014,25 @@ return (function () {
|
||||
var splitPath = path.split("#");
|
||||
var pathNoAnchor = splitPath[0];
|
||||
var anchor = splitPath[1];
|
||||
var finalPathForGet = null;
|
||||
if (verb === 'get' || verb === 'delete') {
|
||||
finalPathForGet = pathNoAnchor;
|
||||
|
||||
var finalPath = path
|
||||
if (useUrlParams) {
|
||||
finalPath = pathNoAnchor;
|
||||
var values = Object.keys(filteredParameters).length !== 0;
|
||||
if (values) {
|
||||
if (finalPathForGet.indexOf("?") < 0) {
|
||||
finalPathForGet += "?";
|
||||
if (finalPath.indexOf("?") < 0) {
|
||||
finalPath += "?";
|
||||
} else {
|
||||
finalPathForGet += "&";
|
||||
finalPath += "&";
|
||||
}
|
||||
finalPathForGet += urlEncode(filteredParameters);
|
||||
finalPath += urlEncode(filteredParameters);
|
||||
if (anchor) {
|
||||
finalPathForGet += "#" + anchor;
|
||||
finalPath += "#" + anchor;
|
||||
}
|
||||
}
|
||||
xhr.open(verb.toUpperCase(), finalPathForGet, true);
|
||||
} else {
|
||||
xhr.open(verb.toUpperCase(), path, true);
|
||||
}
|
||||
|
||||
xhr.open(verb.toUpperCase(), finalPath, true);
|
||||
xhr.overrideMimeType("text/html");
|
||||
xhr.withCredentials = requestConfig.withCredentials;
|
||||
xhr.timeout = requestConfig.timeout;
|
||||
@ -3048,7 +3053,7 @@ return (function () {
|
||||
xhr: xhr, target: target, requestConfig: requestConfig, etc: etc, boosted: eltIsBoosted,
|
||||
pathInfo: {
|
||||
requestPath: path,
|
||||
finalRequestPath: finalPathForGet || path,
|
||||
finalRequestPath: finalPath,
|
||||
anchor: anchor
|
||||
}
|
||||
};
|
||||
@ -3123,7 +3128,8 @@ return (function () {
|
||||
});
|
||||
});
|
||||
triggerEvent(elt, 'htmx:beforeSend', responseInfo);
|
||||
xhr.send(verb === 'get' ? null : encodeParamsForBody(xhr, elt, filteredParameters));
|
||||
var params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredParameters)
|
||||
xhr.send(params);
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
@ -177,5 +177,59 @@ describe("Core htmx Parameter Handling", function() {
|
||||
vals['do'].should.equal('rey');
|
||||
})
|
||||
|
||||
it('it puts GET params in the URL by default', function () {
|
||||
this.server.respondWith("GET", "/test?i1=value", function (xhr) {
|
||||
xhr.respond(200, {}, "Clicked!");
|
||||
});
|
||||
var form = make('<form hx-trigger="click" hx-get="/test"><input name="i1" value="value"/><button id="b1">Click Me!</button></form>');
|
||||
form.click();
|
||||
this.server.respond();
|
||||
form.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('it puts GET params in the body if methodsThatUseUrlParams is empty', function () {
|
||||
this.server.respondWith("GET", "/test", function (xhr) {
|
||||
xhr.requestBody.should.equal("i1=value");
|
||||
xhr.respond(200, {}, "Clicked!");
|
||||
});
|
||||
var form = make('<form hx-trigger="click" hx-get="/test"><input name="i1" value="value"/><button id="b1">Click Me!</button></form>');
|
||||
|
||||
try {
|
||||
htmx.config.methodsThatUseUrlParams = [];
|
||||
form.click();
|
||||
this.server.respond();
|
||||
form.innerHTML.should.equal("Clicked!");
|
||||
} finally {
|
||||
htmx.config.methodsThatUseUrlParams = ["get"];
|
||||
}
|
||||
});
|
||||
|
||||
it('it puts DELETE params in the body by default', function () {
|
||||
this.server.respondWith("DELETE", "/test", function (xhr) {
|
||||
xhr.requestBody.should.equal("i1=value");
|
||||
xhr.respond(200, {}, "Clicked!");
|
||||
});
|
||||
var form = make('<form hx-trigger="click" hx-delete="/test"><input name="i1" value="value"/><button id="b1">Click Me!</button></form>');
|
||||
form.click();
|
||||
this.server.respond();
|
||||
form.innerHTML.should.equal("Clicked!");
|
||||
});
|
||||
|
||||
it('it puts DELETE params in the URL if methodsThatUseUrlParams contains "delete"', function () {
|
||||
this.server.respondWith("DELETE", "/test?i1=value", function (xhr) {
|
||||
xhr.respond(200, {}, "Clicked!");
|
||||
});
|
||||
var form = make('<form hx-trigger="click" hx-delete="/test"><input name="i1" value="value"/><button id="b1">Click Me!</button></form>');
|
||||
|
||||
try {
|
||||
htmx.config.methodsThatUseUrlParams.push("delete")
|
||||
form.click();
|
||||
this.server.respond();
|
||||
form.innerHTML.should.equal("Clicked!");
|
||||
} finally {
|
||||
htmx.config.methodsThatUseUrlParams = ["get"];
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
//
|
||||
describe("json-enc extension", function() {
|
||||
beforeEach(function () {
|
||||
this.server = makeServer();
|
||||
@ -9,6 +8,15 @@ describe("json-enc extension", function() {
|
||||
clearWorkArea();
|
||||
});
|
||||
|
||||
it('handles basic get properly', function () {
|
||||
var jsonResponseBody = JSON.stringify({});
|
||||
this.server.respondWith("GET", "/test", jsonResponseBody);
|
||||
var div = make('<div hx-get="/test" hx-ext="json-enc">click me</div>');
|
||||
div.click();
|
||||
this.server.respond();
|
||||
this.server.lastRequest.response.should.equal("{}");
|
||||
})
|
||||
|
||||
it('handles basic post properly', function () {
|
||||
var jsonResponseBody = JSON.stringify({});
|
||||
this.server.respondWith("POST", "/test", jsonResponseBody);
|
||||
@ -67,7 +75,6 @@ describe("json-enc extension", function() {
|
||||
})
|
||||
|
||||
it('handles put with form parameters', function () {
|
||||
|
||||
this.server.respondWith("PUT", "/test", function (xhr) {
|
||||
var values = JSON.parse(xhr.requestBody);
|
||||
values.should.have.keys("username","password");
|
||||
@ -109,7 +116,7 @@ describe("json-enc extension", function() {
|
||||
this.server.lastRequest.response.should.equal('{"passwordok":true}');
|
||||
})
|
||||
|
||||
it.skip('handles delete with form parameters', function () {
|
||||
it('handles delete with form parameters', function () {
|
||||
|
||||
this.server.respondWith("DELETE", "/test", function (xhr) {
|
||||
var values = JSON.parse(xhr.requestBody);
|
||||
|
@ -1517,6 +1517,7 @@ listed below:
|
||||
| `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](@/attributes/hx-swap.md#focus-scroll) swap modifier. |
|
||||
| `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will include a cache-busting parameter in `GET` requests to avoid caching partial responses by the browser |
|
||||
| `htmx.config.globalViewTransitions` | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content. |
|
||||
| `htmx.config.methodsThatUseUrlParams` | defaults to `["get"]`, htmx will format requests with this method by encoding their parameters in the URL, not the request body |
|
||||
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user