express
express copied to clipboard
Optional custom json replacer/spaces per response
In some cases it is useful to be able to overwrite the json replacer at response level, in the end it is something related to the response.
This change also makes it possible to simply say: res.jsonReplacer = null; res.json(data);
to prevent the jsonReplacer from kicking in.
Please come up with a different API, as adding a magical property to res
is not what we want.
Please also read through a previous request at https://github.com/strongloop/express/pull/2098 and, if you can, provide a sample use-case for what the per-request replacer function you are trying to use.
Here is a better idea:
app.get('/', function() {
res.body = { '...': 'any json you want' } // use that instead of res.json()
res.jsonReplacer = function(){ /* whatever */ }
next()
})
app.use(function(req, res, next) {
if (!res.body) return next()
res.send(JSON.stringify(res.body, res.jsonReplacer))
})
Which doesn't require any changes to the core at all.
I don't think we want to use res.send for json.
Hi @rubenstolk , have you had time to come up with a different API yet?
Not yet, but I will, hopefully have some time this weekend.
Ruben Stolk
CHANGER, Raising the bar in Online Experience!
+91 95 6105 0034 +31 6 46 11 80 37
INDIA OFFICE 308 Sky Max, Datta Mandir Chowk, Viman Nagar, Pune – 411014, India ( http://goo.gl/maps/muZw7 http://g.co/maps/g9y7n) +91 20 65 7373 33
NETHERLANDS OFFICE Maerten Trompstraat 25, 2628 RC Delft, Netherlands (http://goo.gl/QQHfWE http://g.co/maps/tcpwn) +31 88 1001300
http://changer.nl
On Fri, Oct 31, 2014 at 7:06 PM, Douglas Christopher Wilson < [email protected]> wrote:
Hi @rubenstolk https://github.com/rubenstolk , have you had time to come up with a different API yet?
— Reply to this email directly or view it on GitHub https://github.com/strongloop/express/pull/2422#issuecomment-61260391.
If you really think it is a good idea to add it to express itself, the best api as of now would be this:
res.set('json replacer', function(){})
res.json(data)
Thus, allowing to override any app.set()
with res.set()
for an individual request.
I'd totally went for it in express@3
. But express becomes more modular nowadays, and now I doubt that replacer
is even worth having here.
I don't like the idea of res.set('json replacer', function(){})
Mainly because set/get probably will not not exist in 5 and this is additional overhead.
Can we do res.json(data, replacer)
?
Can we do res.json(data, replacer)?
res.type('json').send(JSON.stringify(data, replacer))
Otherwise res.json(data, replacer)
is ambiguous, because data
could be a number and replacer
could be null
, which is why they form has been turned down before.
Otherwise res.json(data, replacer) is ambiguous, because data could be a number and replacer could be null, which is why they form has been turned down before.
em, pardon?
Isn't json(num, data) being removed in 5.x?
I was under the impression this PR was a feature request for 4, since that is where is PR is based. @rubenstolk what version are you requesting this feature in?
@dougwilson I'm not sure it matters.
I'm wondering where we can implement the most reasonable interface for the feature.
@Fishrock123 I was just saying your suggestion is impossible to implemented in 3/4 is all, as it will break current (deprecated) uses of res.json
.
What is wrong with res.type('json').send(JSON.stringify(data, replacer))
?
Plus I'm still waiting the hear what the use-case is for a per-request json replacer. The provided use-case from the last time this was requested was fundamentally flawed and made no sense, which contributed to why it wasn't added.
What is wrong with
res.type('json').send(JSON.stringify(data, replacer))
?
Nothing actually. I forgot that this was actually stringifying it. >_<
This is the most reasonable approach yeah. I don't think we should add it regardless of use case.
Guys, I wasn't aware of the fact that we could use .get
and .set
on res
as well, so I've changed that at least. I think current solution is really good and pragmatic. You can now either set the json replacer
on app level or on response level.
My use case:
- We have an app that has a 'global'
json replacer
that strips out certain unwanted properties from all of our models (for example, mongoose models icwtoObject
always results in__v
andid
properties which we do not want in our json), so we strip them out. - Now, on one of our api's (a legacy one used by and older production version of a client) needs to have the
id
property. Since we currently only have the option to definejson replacer
at app level, we can't override this. (Except using the customres.send
as suggested by some people above, but I really don't like that because our framework is fully convention based and our generic api-layer takes care of theres.json
). - My preferred suggestion: accept this pull request, it doesn't harm anything but allows more flexibility.
- My alternative suggestion: remove
json replacer
from app level, but come up with an alternative to make the behavior ofres.json
overridable. Currently theres.json
method does not allow for customizing the behavior at all, which is limiting imho.
Guys, I wasn't aware of the fact that we could use .get and .set on res as well, so I've changed that at least.
You... can't. It was a suggestion for a possible implementation, but you'd have to fully implement res.set
and res.get
...
OK got it, I'll come up with something else then, give me more time.
Uhm, why is it working then?
Because res.get('json replacer')
calls res.getHeader('json replacer')
which is just returning undefined
, since there is no json replacer
header set. You never tried to add tests for the feature you added to notice?
Using your code right now, the following app:
var express = require('./index')
var app = express()
app.set('json replacer', function (key, value) {
if (key === 'id') return
return value
})
app.use(function (req, res) {
res.set('json replacer', function (key, value) {
if (key === 'secret') return
return value
})
res.json({
id: 3,
secret: 'keyboardcat',
name: 'loki'
})
})
app.listen(3000)
Results in the following HTTP response:
$ curl -i http://localhost:3000/
HTTP/1.1 200 OK
X-Powered-By: Express
json replacer: function (key, value) {if (key === 'secret') returnreturn value}
Content-Type: application/json; charset=utf-8
Content-Length: 38
ETag: W/"26-3a6fc2e6"
Date: Sat, 01 Nov 2014 03:55:17 GMT
Connection: keep-alive
{"secret":"keyboardcat","name":"loki"}
Notice how the json replace I tried to set with res.set
not only didn't apply to the body (because the "secret" key is still there), but the body of the function appears as a response header.
Oh, sorry @rubenstolk I didn't realized you replied. You can just make a new comment rather than replying by completely replacing the content of an old comment, which is really confusing and doesn't let me know you made a reply.
Oh, and FYI, to me the use-case for adding this I guess sounds valid, though using express to do your DTO -> DTO transformations seems a little heavy-handed to me.
Well, I'm not using express per se for it, but express has a json replacer
setting which is meant for this purpose right?
@dougwilson What do you think about the current approach? A separate 'createJSON' method which is used by both res.json
and res.jsonp
. Now we can just override createJSON
on res if needed which will solve my need.
Interested to hear if this is going to work out or not. FYI, currently I'm living with this patch in my code:
// TODO: this is a non desired override because it might not survive express updates
// Hopefully https://github.com/strongloop/express/pull/2422 works out
app.response.__proto__.json = function (obj) { // jshint ignore:line
var replacer = typeof this.jsonReplacer !== 'undefined' ?
this.jsonReplacer : this.app.get('json replacer');
var spaces = typeof this.jsonSpaces !== 'undefined' ?
this.jsonSpaces : this.app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces);
if (!this.get('Content-Type')) {
this.set('Content-Type', 'application/json');
}
return this.send(body);
};
So, it's still open because we need to address this (the issue about the replacer/spaces scope) in some way, but I'm not sure the current implementation in the PR would work out, i.m.o.
While I don't have a use for setting json replacer
on the response level, I do have a use for setting json spaces
. I'm not sure there is a great way to implement this without messing up the API but, I'm thinking about something along the lines of
res
.config({
"json replacer" : function() {...},
"json spaces" : 0
})
.json(data);
Basically, .config
could be used to set properties of the response that are not headers. Do you think something like that would be consistent enough with the API?
Likewise, I could see something like
res
.config("json spaces", 0)
.json(data);
+1. This would be very helpful when escaping output from certain requests, and not from others.