I'm trying to create a very simple case: a controller action which renders a static JSON, from a template.
controller:
defmodule MyApp.TestController do
use Phoenix.Controller
def show(conn, _params) do
render(conn, "show.json")
end
end
view:
defmodule MyApp.TestView do
use MyApp.Web, :view
end
show.json.eex:
{
"message": "Hello, world!"
}
The problem is, I get the proper JSON response, but JSON-encoded:
"{\n \"message\": \"Hello, world!\"\n}"
Any idea why, and how to solve it?
/Edit:
I found out that I can work around the problem by renaming the template to something other than json (plus explicitly setting response type, of course), so obviously JSON templates are additionally encoded. But why, who would want such a thing?
After further investigation and talking with people on Phoenix Slack channel, I have a clearer picture about what's going on:
Phoenix is agnostic when it comes to deciding whether the content comes from a template or from a data structure in the view. render/2 from the controller happily takes anything and converts it to JSON.
I wrote up a more detailed blog post about this issue, along with several approaches in addressing it, and in my opinion every is a workaround for the inherent problem in Phoenix (which, admittedly, is far from critical).
Essentially, one should avoid executing Poison.encode function, which gets called from render_to_iostream function. You can do that either by not using .json as the template extension, by directly calling Phoenix.View.render from the controller, or by creating custom encoder and template engine to pass some metadata along with the data to be output.
Related
I am working on play framework with SCALA as backend.
Json data is given to the front end from the controller.
I want to add HTML as value of some fields of json.
This HTML will be kept as a template and data will be added in this template at run time.
I think i should put unique names in the HTML template and then these names will be replaced by the data which i want to add at run time. Ultimately, this HTML will be added in the json response.
Is my approach right? If not, what is the best approach to add data in an HTML template,add this template in json response and send this combined response to the front-end for further use?
Is it a good practice to use string replacement to add data in an HTML template?
I think as long as you use Play, you can put your HTML templates into app/views package. Let's say you call your template mytemplate.scala.html
You can parameterize this view as any Play view.
In the place in your code where you generate your JSON response you can then call mytemplate(parameters) to get html generated, Play will do all the work here for you. Then using play.api.libs.json.JSON object's methods and related facilities you can convert this html to JSON.
So in your controller's code you will have something like Ok(JSON.toJson(mytemplate(parameters)))
This is of course a sketch, so you will need to elaborate and try.
Here's my JSON response for http://localhost:8000/characters/api/users/1?format=json
)]}',
{"id":1,"username":"admin","mage_by_user":[3],"mage_last_updated":"2015-02-11T16:13:16.229Z"}
Notice the )]}', on the first line.
Here is my code that gets called to create the JSON:
class UserSerializer(serializers.ModelSerializer):
mage_by_user = serializers.PrimaryKeyRelatedField(
many=True, queryset=Mage.objects.all())
mage_last_updated = serializers.ReadOnlyField(
source='mage_by_user.updated_date')
class Meta:
model = User
fields = ('id', 'username', 'mage_by_user', 'mage_last_updated',)
Further testing:
I've noticed the title of the page is TypeError at <insert url here>.
This happens with all of my endpoints
If I try to access a non-existent object (userId=2 for instance), then renders 'normally' for DRF, e.g:
{
detail: "Not found"
}
Any idea why this would happen?
Those characters are inserted by the Djangular middleware AngularJsonVulnerabilityMiddleware, to inject Json Vulnerability Protection
A JSON vulnerability allows third party website to turn your JSON resource URL into JSONP request under some conditions. To counter this your server can prefix all JSON requests with following string ")]}',\n". Angular will automatically strip the prefix before processing it as JSON.
Unfortunately, it means it breaks various JSON viewers.
Sorry to not be more help, but this looks like something entirely unrelated to REST framework. There's absolutely no way a JSON response there would ever be rendered in that way.
Perhaps you have a custom renderer configured, that's outputting a malformed response, perhaps you have some broken middleware inserting those characters, perhaps its an issue in the client or whatever environment you're making the requests, or perhaps it's something else entirely unrelated to any of those.
I'd start by trying to narrow down the issue as much as possible - remove all the complexity from the view and serializer and attempt to replicate the behavior in a test case.
Most likely there's some sort of unexpected integration issue you're missing or some otherwise obvious code typo that's being overlooked.
Ok, this is my Nth question regarding this topic, and I'm getting really frustrated with Grails. Please have a quick look on one of my earlier questions for more details.
Among other things, my problem is that sending JSON formatted data to the controller when testing doesn't seem to work. The controller doesn't get null object, but the argument passed is practically empty--the JSON properties don't get set.
Aside from the controller code from the link above, I also tried,
def save() {
def model = new MyModel(request.JSON)
model.save()
}
but it still fails to set properties.
From my Web searches, I read that in older versions, parseRequest must be set to true in UrlMapping.groovy so that request data formatted in XML, JSON, etc. would automatically be parsed and passed as controller method argument. I'm working on Grails 2.3.9, and I'm not sure if it's still necessary to do that.
The time I thought I'd save if I use Grails on this project is being spent on looking for an answer to this seemingly simple task of testing a RESTful Web service.
No since 2.3.0 the parseRequest option doesn't do anything. The request is parsed lazily only when request.XML or request.JSON is accessed or when binding to a command object.
I want to set an arbitrary attribute for rendering to JSON.
I had followed the answer in this question: how to append data to json in ruby/rails? to do
model = Model.find
model[:extra_info] = "More detail."
model.to_json
It works perfectly, but in my tests I'm getting a deprecation warning that setting arbitrary attributes is no longer supported, use attr_writer.
I tried using
model.write_attribute(:extra_info, "More detail.")
which works in unit testing, but on the server, raises an exception:
private method `write_attribute' called for Model
What's the non-deprecated clean way to do this.
I'm aware I could set it in the JSON call with methods as in Add virtual attribute to json output, but in this case the variable to be added is not part of the models concern, so it doesn't have access to the data needed to construct the extra attribute, and it would be nasty and messy to do so.
So what's the correct way for the controller to get this data pushed into the model so the JSON renders properly?
In Model model, put
attr_accessor :extra_info
Then in controller
model.extra_info = "more detail"
Nick's answer above is the best in terms of creating well structured, well documented code.
Update
*It seems I was wrong on the below*
The code below still creates deprecation warnings on my development server.
In this particular case, I don't want to clutter the model up with extra accessors for very specific once off cases, so I'm using
s.send(:write_attribute, :extra_info, "more detail")
Inside a helper, inside the controller.
I'm developing a RESTful interface which is used to provide JSON data for a JavaScript application.
On the server side I use Grails 1.3.7 and use GORM Domain Objects for persistence. I implemented a custom JSON Marshaller to support marshalling the nested domain objects
Here are sample domain objects:
class SampleDomain {
static mapping = { nest2 cascade: 'all' }
String someString
SampleDomainNested nest2
}
and
class SampleDomainNested {
String someField
}
The SampleDomain resource is published under the URL /rs/sample/ so /rs/sample/1 points to the SampleDomain object with ID 1
When I render the resource using my custom json marshaller (GET on /rs/sample/1), I get the following data:
{
"someString" : "somevalue1",
"nest2" : {
"someField" : "someothervalue"
}
}
which is exactly what I want.
Now comes the problem: I try to send the same data to the resource /rs/sample/1 via PUT.
To bind the json data to the Domain Object, the controller handling the request calls def domain = SampleDomain.get(id) and domain.properties = data where data is the unmarshalled object.
The binding for the "someString" field is working just fine, but the nested object is not populated using the nested data so I get an error that the property "nest2" is null, which is not allowed.
I already tried implementing a custom PropertyEditorSupport as well as a StructuredPropertyEditor and register the editor for the class.
Strangely, the editor only gets called when I supply non-nested values. So when I send the following to the server via PUT (which doesn't make any sense ;) )
{
"someString" : "somevalue1",
"nest2" : "test"
}
at least the property editor gets called.
I looked at the code of the GrailsDataBinder. I found out that setting properties of an association seems to work by specifying the path of the association instead of providing a map, so the following works as well:
{
"someString" : "somevalue1",
"nest2.somefield" : "someothervalue"
}
but this doesn't help me since I don't want to implement a custom JavaScript to JSON object serializer.
Is it possible to use Grails data binding using nested maps? Or do I really heave to implement that by hand for each domain class?
Thanks a lot,
Martin
Since this question got upvoted several times I would like to share what I did in the end:
Since I had some more requirements to be implemented like security etc. I implemented a service layer which hides the domain objects from the controllers. I introduced a "dynamic DTO layer" which translates Domain Objects to Groovy Maps which can be serialized easily using the standard serializers and which implements the updates manually. All the semi-automatic/meta-programming/command pattern/... based solutions I tried to implement failed at some point, mostly resulting in strange GORM errors or a lot of configuration code (and a lot of frustration). The update and serialization methods for the DTOs are fairly straightforward and could be implemented very quickly. It does not introduce a lot of duplicate code as well since you have to specify how your domain objects are serialized anyway if you don't want to publish your internal domain object structure. Maybe it's not the most elegant solution but it was the only solution which really worked for me. It also allows me to implement batch updates since the update logic is not connected to the http requests any more.
However I must say that I don't think that grails is the appropriate tech stack best suited for this kind of application, since it makes your application very heavy-weight and inflexbile. My experience is that once you start doing things which are not supported by the framework by default, it starts getting messy. Furthermore, I don't like the fact that the "repository" layer in grails essentially only exists as a part of the domain objects which introduced a lot of problems and resulted in several "proxy services" emulating a repository layer. If you start building an application using a json rest interface, I would suggest to either go for a very light-weight technology like node.js or, if you want to/have to stick to a java based stack, use standard spring framework + spring mvc + spring data with a nice and clean dto layer (this is what I've migrated to and it works like a charm). You don't have to write a lot of boilerplate code and you are completely in control of what's actually happening. Furthermore you get strong typing which increases developer productivity as well as maintainability and which legitimates the additional LOCs. And of course strong typing means strong tooling!
I started writing a blog entry describing the architecture I came up with (with a sample project of course), however I don't have a lot of time right now to finish it. When it's done I'm going to link to it here for reference.
Hope this can serve as inspiration for people experiencing similar problems.
Cheers!
It requires you to provide teh class name:
{ class:"SampleDomain", someString: "abc",
nest2: { class: "SampleDomainNested", someField:"def" }
}
I know, it requires different input that the output it produces.
As I mentioned in the comment earlier, you might be better off using the gson library.
Not sure why you wrote your own json marshaller, with xstream around.
See http://x-stream.github.io/json-tutorial.html
We have been very happy with xstream for our back end (grails based) services and this way you can render marshall in xml or json, or override the default marshalling for a specific object if you like.
Jettison seems to produce a more compact less human readable JSON and you can run into some library collision stuff, but the default internal json stream renderer is decent.
If you are going to publish the service to the public, you will want to take the time to return appropriate HTTP protocol responses for errors etc... ($.02)