I have a Grails app running on 1.3.7 with the json-rest-api plugin version 1.0.8 installed. I'm trying to post data from the command line using curl in order to create a new instance of a domain class, but cannot seem to figure out the correct way to format the JSON so that the parser is satisfied. I can't find any documentation either that would conclusively describe how the data is supposed to be formatted.
The domain class is defined like this:
package foo
class Foo {
static expose = 'foo'
String name
static constraints = {
name(inList: ['xyzzy', 'quux'])
}
}
Given the above, this doesn't work:
$ curl -X POST --header "Content-Type: application/json" --data-urlencode '{"name":"xyzzy"}' http://myapp/api/foo
The app returns a 500 error code and the content of the reply is
{"success":false,"message":"Property [name] of class [class foo.Foo] cannot be null"}
What kind of data does the API expect me to send?
Try the following format:
{ data: { name: "Blabla" } }
This isn't specific to the json-rest API. I've only ever used the built in RESTful capabilities of Grails.
You need your URL Mapping so that parseRequest = true. You have to include the class in your JSON:
{"class":"Foo", "name":"blabla"}
In your controller, you do this...
def fooInstance = new Foo(params['foo'])
And your gold.
Related
What is the equivalent JSON request for querying a spatial location field in Apache Solr to:
&fq={!bbox sfield=locfield}&pt=45.15,-93.85&d=5
You can use a params block.
Parameters placed in a params block act as if they were added verbatim
to the query-parameters of the request.
For example using curl :
curl "http://hostname:8983/solr/corename/query?" -d '
{
params: {
q: "*:*",
fq: "{!bbox sfield=locfield}",
pt: "45.15,-93.85",
d: "5"
}
}'
#see JSON Request API Supported Properties and Syntax
Currently I play around with iron:router's solution for a restful API. For this I use the .put, .get ... methods which are iron:router has implemented.
This is my example I work with:
Router.route('/api', {where:'server'})
.put(function(){
var req;
req = this.request;
console.log(req.body);
this.response.end('PUT finished.');
});
When I execute the following I will get the expected response (PUT finished):
curl -XPUT "http://localhost:4000/api " -d'{"name": "timo"}'
But the console.log(req.body) returns a strange value converted to an object.
The returned value is:
{
'{"name": "timo"}\n' : ''
}
It seems that iron:router trys to convert the body into an object but did not recognized that the given request string is a valid JSON string.
Is there smth I did wrong ? I did not find anything helpful yet to prepare iron:router that the given request body is still JSON.
Maybe its a better solution not to tell iron:router that the given request is a JSON and instead to tell iron:router that it shouldn't do anything so I can convert the JSON string by myself ?
You didn't specify the content type in your curl request. Try this instead:
curl -XPUT "http://localhost:4000/api " -d'{"name": "timo"}' -H "content-type: application/json"
With that it works for me:
I20150522-09:59:08.527(-7)? Request { name: 'timo' }
(and without it doesn't).
i'm writing a Play 2.3.2 application using Scala.
In my controller i had a method which get the json object from the request.
The implementation is like the following:
def firstTags = Action.async { request =>
def elaborate(n: Int): Future[Result] = {//some implementation}
//get the json Object
val jsonObject = request.body.asJson
//parse the json object
jsonObject match {
case Some(json) => json \ "number" match {
case x: JsNumber => elaborate(x.as[Int])
case _ => Future{BadRequest}
}
case None => Future{BadRequest("Need a Json")}
}
}
In my route files i had:
GET /recommendation/statistic/first #recommendationsystem.controllers.manager.StatisticsController.firstTags
When i try to call the method with the curl i get always a "Need a Json" response.
I call my server with curl like the following:
curl -H "Accept: application/json" -H "Content-Type: application/json" -d '{"number": 3}' -X GET http://localhost:9000/recommendation/statistic/first
What's wrong??
GET shouldn't have body. look at HTTP GET with request body.
POST method is not only for modify the server state, but also to process data.
From RFC 2616 - HTTP/1.1:
The POST method is used to request that the origin server accept the
entity enclosed in the request as a new subordinate of the resource
identified by the Request-URI in the Request-Line. POST is designed
to allow a uniform method to cover the following functions:
Annotation of existing resources;
Posting a message to a bulletin board, newsgroup, mailing list,
or similar group of articles;
Providing a block of data, such as the result of submitting a
form, to a data-handling process;
Extending a database through an append operation.
The actual function performed by the POST method is determined by the
server and is usually dependent on the Request-URI. [...]
I want to send a request as JSON and in my controller I want to parse this JSON and get the parameters I want. for example this is the request:
{"param1":"val1"}
I want to parse this request and get "param1" value. I used request.JSON but still I got null. Is there any other way to solve this?
Thanks,
You can use one of the following to test your stuff (both options could be re-used as automated tests eventually - unit and integration):
write a unit test for you controller like (no need to start the server):
void testConsume() {
request.json = '{param1: "val1"}'
controller.consume()
assert response.text == "val1"
}
and let's say your controller.consume() does something like:
def consume() {
render request.JSON.param1
}
Or you can use for example the Jersey Client to do a call against your controller, deployed this time:
public void testRequest() {
// init the client
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
// create a resource
WebResource service = client.resource(UriBuilder.fromUri("your request url").build());
// set content type and do a POST, which will accept a text/plain response as well
service.type(MediaType.APPLICATION_JSON).accept(MediaType.TEXT_PLAIN).put(Foo.class, foo);
}
, where foo is a Foo like this:
#XmlRootElement
public class Foo {
#XmlElement(name = "param1")
String param1;
public Foo(String val){param1 = val;}
}
Here are some more examples on how to use the Jersey client for various REST requests:
https://github.com/tavibolog/TodaySoftMag/blob/master/src/test/java/com/todaysoftmag/examples/rest/BookServiceTest.java
Set it in your UrlMappings like this:
static mappings = {
"/rest/myAction" (controller: "myController", action: "myAction", parseRequest: true)
}
Search for parseRequest in latest Grails guide.
Then validate if it works correctly with curl:
curl --data '{"param1":"value1"}' --header "Content-Type: application/json" http://yourhost:8080/rest/myAction
In the controller method, check request.format. It should specify json. I'm guessing it won't here, but it may give you clues as to how your payload is being interpreted.
In your Config.groovy file, I would set the following values:
grails.mime.file.extensions = false
grails.mime.use.accept.header = false
In that same file, check your grails.mime.types. make sure it includes json: ['application/json', 'text/json'], which it probably will, but put it above */*. These entries are evaluated in order (this was true in pre 2.1 versions, havent' verified it is now, but what the heck). In conjunction with that, as aiolos mentioned, set your content-type header to one of the above mime-types.
Finally, test with curl, per Tomasz KalkosiĆski, or, to use RESTClient for FF, click on "Headers" in the very top of the client page (there are 4 clickable items at the top-left; headers is one. From a fresh RESTClient, you may have to choose "Custom Header". I can't recall)
I'm working on a RESTful application using CodeIgniter and I'm unable to access my POST'd json data in my controller.
I'm posting the json via cURL on my local machine, while the app is being developed on a remote server.
Here is the controller code in question:
class Products extends CI_Controller
{
public function __construct()
{
$this->load->model(products_model);
}
public function index($id = FALSE)
{
if($_SERVER['REQUEST_METHOD'] == 'GET')
{
// fetch product data
$product_data = $this->products_model->get_products($id)
// set appropriate header, output json
$this->output
->set_content_type(application/json)
->set_output(json_encode($product_data));
}
elseif($_SERVER['REQUEST_METHOD'] == 'POST')
{
// debugging for now, just dump the post data
var_dump($this->input->post());
}
}
}
The GET action is working well enough, and returning the appropriate data when requested from a browser, or via a cURL request. However, when attempting to POST json data via cURL I consistently get bool(FALSE) returned from the POST section of the index function. Here is the cURL request I'm making:
curl -X POST -d #product.json mydomain.com/restfulservice/products
Also, here is the contents of the product.json file:
{"id":"240",
"name":"4 x 6 Print",
"cost":"1.5900",
"minResolution":401,
"unitOfMeasure":"in",
"dimX":0,
"dimY":0,
"height":4,
"width":6}
I've made another POST via cURL, excluding the json data and passing something like this:
curl -X POST -d '&this=that' mydomain.com/restfulservice/products
Which returns
array(1) {
["this"]=>
string(4) "that"
}
What gives? Something with the json? It's valid. I've turned off the global CSRF and XSS in application/config/config.php as I understand they require use of CI's form_open() and won't work properly without it. It's my understanding that excluding parameters from $this->input->post() will return ALL the post items yet I continue to get none. I've also tried going around CI's input library and accessing the data via PHP's $_POST variable, it has made no difference.
Your post data is not in query string format so you should skip dealing with $_POST and go straight to the raw post data.
try
var_dump($HTTP_RAW_POST_DATA);
or even better
var_dump(file_get_contents("php://input"));
in codeigniter 2.X, you can override Input class and add necessary functionality.
https://ellislab.com/codeigniter/user-guide/general/core_classes.html
add file MY_Input.php to application/core
add code inside this file:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Input extends CI_Input {
public function raw_post() {
return file_get_contents('php://input');
}
public function post($index = NULL, $xss_clean = FALSE) {
$content_type = $this->get_request_header('Content-type');
if (stripos($content_type, 'application/json') !== FALSE
&& ($postdata = $this->raw_post())
&& in_array($postdata[0], array('{', '['))) {
$decoded_postdata = json_decode($postdata, true);
if ((json_last_error() == JSON_ERROR_NONE))
$_POST = $decoded_postdata;
}
return parent::post($index, $xss_clean);
}
}
that's it..
use it like normal post..