Mithril.js: m.request should provide option for interpolate when having plain-text body

Created on 10 Mar 2017  路  21Comments  路  Source: MithrilJS/mithril.js

When using m.request(), it is able to have replacable entries inside the URL.

m.request({
    method: "PUT",
    url: "/api/v1/users/:id",
    data: {id: 1, name: "test"}
})
.then(function(result) {
    console.log(result)
})

I want to create an array with my URLs, containing the interpolate-parts, but having string as POST-body. How can I have URL-interpolation with plain-text as POST-body?

The idea is to have URLs all at one place, and using POST without JSON-body, but having the IDs being part of the URL-strings.

var REST_URLS = {
    someMethod: "/some/path/:id/add"
};

m.request({
    method: "POST",
    url: REST_URLS.someMethod,
    data: "plain-text-body",
    interpolationdata: {
        "id": 123
    }
})
.then(function(result) {
    console.log(result)
})

Maybe this can be made via custom options.serialize ? Some advice would be nice.

Breaking Change

All 21 comments

It feels a bit unclean that interpolation-data and real data is mixed in the same object, how do you guys think about this?

This seems possible to handle without any particular support from the m.request API directly;

// ids is an array of ids
function doAllTheRequests(ids) {
  return Promise.all(ids.map(function(id) {
    return m.request({
     method: "POST", 
     url : "/some/path/" + id + "/add,
     serialize : function (value) { return value }
   }
  })
}

Additionally you could write some kind of interpolation helper for the URL part itself if it's more complex.

Hi @orbitbot , thanks for your answer. This is exactly what I do not want, because this does not use the useful interpolation.

I could have implement this like this:

return m.request({
    method: "POST", 
    url : REST_URLS.someMethod,
    data: {
        "id": 123,
        "body": "normal-string-body"
    },
    serialize : function (value) { return value.body }
});

But this seems rather unclean, because it is mixing "JavaScript-object" and "JavaScript-string", which I want to avoid. Therefor I asked for guidance, maybe for a second thought about this mixed data-field.

Maybe a solution using Object.assign or picking specific fields from objects using some other helper might feel more appropriate?

Data, as the current API is written is expected to represent the payload of the HTTP request. I feel this case is more appropriately covered by using some helper rather than increasing the m.request API surface, at least based on this specific use case.

Data, as the current API is written is expected to represent the payload of the HTTP request.

Thats not fully true, this is not only the payload, but used for replacing parts of the URL too.

Some example:

var someData = {
    "id": 123
};

When having this URL /some/api/path/:id/subresource , this means that it is part of the POST-body AND the URL, which seams a bit unclean. It is a special thing from mithril, maybe this can be adjusted to have these two information being separated.

My usecase: I have something like /container/:uuid/subitems/:subitemuuid/subsubitem/add as URL, but the entries "uuid" and "subitemuuid" are not part of the plain-text/non-json body. Having the whole string /container/:uuid/subitems/:subitemuuid/subsubitem/add being stored somewhere more readable at one place makes it hard to replace the parts, when it is already provided by mithril.

OK, I have to admit I wasn't aware of that interpolation part of the existing API.

So, in this case, what is your actual consumption API (eg. that you use elsewhere)?

it is mixing "JavaScript-object" and "JavaScript-string", which I want to avoid.

Is this aesthetics or a problem when you call the sending method?

It is both, aesthetics and a problem with the recieving side, which is setup to be very picky about the content, I'm having some JAX-WS Endpoint which uses JAXB for de-serialization, and it is sometimes mixed with normal strings instead of deserialized stuff. Nothing more that I can tell about, sorry.

Despite of aestethics, this might result in some problem for others when something like "uuid" is existing in the URL and being part of the deserialized JSON as object.

Maybe this can be split like this:

    url : {
        "raw": REST_URLS.someMethod,
        "urldata": {
            "id": 123
        }
    },
    data: "normal-string-body", // or even some object with different ID

This would remove the problem with mixing two different type of data-areas.

I had a similar idea, and it would be fairly simple to check for the url parameter being a string or object. However, I guess the separated API doesn't help in the cases where presumably someone has intentionally keyed the URL to match data fields...

Thats true, but in that case, the normal interpolation part would take place. My suggestion is just some kind of addition, where the data is not part of the POST-body. 馃樅 nothing would break, just being added/enhanced

There is one special scenario which I don't know how to solve (in case of some additional interpolation-way):

    url : {
        "raw": REST_URLS.someMethod,
        "urldata": {
            "id": 123
        }
    },
    data: {
        "id": 456
    }

Using current version, the normal data.id would be used, which seems legit (as it is the prefered way right now), but having this url.urldata.id might override it ... don't know if this would be something wanted, would make a lot of people being confused maybe...

Leaving aside the actual implementation suggestion, which I guess could be solved by some rules on which object value to consider, you could also use the serialize method to completely sidestep whatever you have in the data parameter, eg. serialize : function() { return someOtherValue }. Since you seem to have a quite involved case, at face value it seems like having a (set of) helper method(s) for updating the server that abstracts the actual implementation might be a good idea.

I agree, this can be solved via a specialized serialize-method, but as there is a lot of effort for having a clean API, it surprises me that this interpolation-feature is using the data itself, I even would think that this data would be removed from the data (as it already is used). Maybe this is the main confusing part for me, that its used as "data" AND "url". Was there some deeper idea behind it being present in both?

BTW, you could do this kind of URL interpolation pretty easily without patching core:

var REST_URLS = {
    someMethod: ({id}) => `/some/path/${id}/add`
};

m.request({
    method: "POST",
    url: REST_URLS.someMethod({id: 123}),
    data: {id: 456}
})
.then(function(result) {
    console.log(result)
})

I've used that kind of pattern myself for other use cases, too, where I could dodge templating libraries entirely in favor of this.

@lhorie I'm pretty stumped on how to address this...

I can tell you the proposals here don't look pretty, but the current API sucks just as much. I could use your guidance on this one.

@isiahmeadows I do know that the proposal is ugly, maybe the more cleaner way would be to remove that interpolation-feature ...

@isiahmeadows Looking at the test cases there are a actually a few that cover the specific case of using data for interpolation in non-GET requests: https://github.com/lhorie/mithril.js/blame/next/request/tests/test-request.js#L86

... by which I deduce that this feature is intentional (also a really old part of the test code, so this was part of the original design). Arguably in the GET case since the data field itself is not used, it would be clearer functionality to have a separate interpolate or urlParams or whatever field to interpolate from, which then other API methods could piggyback on, but then you end up not having the convenience in the POST case.

@orbitbot I'm aware it's intentional, and I stated it as a design error a few days ago on Gitter. That's why I'm kind of stumped on how to handle it - it's technically working as intended, but it's the original intent that I feel is in error for this reason.

;) sorry for poking into this issue ... as I am very new to mithril, this just seemed odd.

@FibreFoX You're okay. @lhorie Ping...

The following PR removes the entries used for interpolation from opts.data: https://github.com/lhorie/mithril.js/pull/1654 focusing GET-requests
The following PR adds a way for separating these two information into two locations (I have to admit: urlParams as additional option now looks way better that having url being an object): https://github.com/lhorie/mithril.js/pull/1702 focusing POST-requests

Using my own serialize-method makes me to force some HEADER, because of this:

if (args.serialize === JSON.stringify && useBody) {
    xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8")
}

As I didn't found anything inside the current documentation (I admit I just could have been blind), this surprised me a bit, which makes me to have TWO adjustments:

  1. my own serialize-method
  2. additional headers

As I'm not watching that gitter-stuff, maybe some fragments of discussion would better be copy-pasted here 馃槃

Resolved by #2361.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pygy picture pygy  路  3Comments

hadihammurabi picture hadihammurabi  路  4Comments

simov picture simov  路  4Comments

josephys picture josephys  路  4Comments

mke21 picture mke21  路  3Comments