I am using Grails 2.3.8 on a Mac.
I hope to use Grails to quickly develop a RESTful API. I started with the basic documentation for Web Services, found here for my version:
http://grails.org/doc/2.3.8/guide/webServices.html#domainResources
This seems to suggest that I can create a simple Domain Class in Grails, then annotate with the #Resource from grails.rest, as follows (straight from the docs above):
import grails.rest.*
#Resource(uri='/books', formats=['json', 'xml'])
class Book {
String title
static constraints = {
title blank:false
}
}
When I place this file into the grails-app/domain directory and run the app, the docs suggest I should be able to use cURL to add "Book" objects via HTTP:
curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Along Came A Spider"}' localhost:8080/myapp/books
But this gives me HTTP 422, with the following message:
HTTP/1.1 422 Unprocessable Entity
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 05 Aug 2014 01:46:05 GMT
{"errors":[{"object":"Book","field":"title","rejected-value":null,"message":"Property [title] of class [class Book] cannot be null"}]}
Can anyone advise as to what I am missing here? I expected this to be pretty straightforward.
As #raffian mentioned above in comment, this was due to a bug in Grails 2.3.8:
https://jira.grails.org/browse/GRAILS-11399
Upgrading resolved the issue.
Related
I am using cURL to execute commands using the REST interface of BaseX like this:
curl http://localhost:8984/rest/?command=repo+list
There are also commands to manage the XQuery module repository. I am especially interested in REPO INSTALL to install a package. Is it somehow possible to execute this command using cURL and the REST interface but without having the package already on the target server? I want to provide the file in the body of the cURL request, similar to adding a resource to a database (source) which goes like this:
curl -i -X PUT -T "etc/xml/factbook.xml" "http://localhost:8984/rest/factbook"
Trying
curl -i -X PUT -T "/tmp/foo.xar" http://localhost:8984/rest/?command=repo+install
Gives me
HTTP/1.1 404 Not Found
Content-Type: text/plain;charset=UTF-8
Content-Length: 18
Connection: close
Server: Jetty(9.4.18.v20190429)
No path specified.
Adding -H "Content-Type: application/x-xar" does not help either.
And replacing PUT with POST gives me
HTTP/1.1 100 Continue
HTTP/1.1 400 Bad Request
Date: Tue, 03 Mar 2020 09:53:21 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 46
Server: Jetty(9.4.18.v20190429)
"" (Line 1): Content is not allowed in prolog.
The following works in case of standard modules (replace user/pass/server if needed):
$ curl http://admin:admin#localhost:8984/rest/?command=repo+install+http://www.xqueryfunctions.com/xq/functx-1.0.1-doc.xq
What would be the best way to post data to Meteor's MongoDB collection ?
I tried initially using the Postman chrome extension, command-line tool like curl but did not find any luck.
The curl command that I use is :
curl -i -X POST -H "Content-Type: application/json" -d '{"hacker":"fromcurl","score":100}' http://localhost:3000/players
OUTPUT:
HTTP/1.1 200 OK
vary: Accept-Encoding
content-type: text/html; charset=utf-8
date: Tue, 17 May 2016 00:37:44 GMT
connection: keep-alive
transfer-encoding: chunked
<!DOCTYPE html>
<html>
PS : players is the collection/table name
Take a look at DDP, Meteor uses this protocol to communicate between client and server. DDP is simple and based on JSON. Meteor’s DDP currently based on WebSockets and SockJS. That's very helpful. I have a case that Websocket cannot work in local LAN network but the connection fall back to SockJS, then everything work fine, that's great!
An example implement DDP communication between Meteor and Python.
Simply create a Meteor method:
Meteor.methods({
clientProcessData: function (data) {
console.log(data);
// Meteor post data to MongoDB code here
},
Follow the instructions from https://github.com/hharnisc/python-meteor to install python-meteor client and connect to Meteor server.
You can call Meteor method from Python by:
client.call('clientProcessData', ["This is a test"], callback_function)
Not sure are there any other DDP clients of other programming language, but in my case, python works great!
// Update: There are many of them, but I'm so new here that afraid of posting external link, so please do a simple "DDP client" search on google.
I've build a demo project on Grails 3.0.10
I can read the data with a REST request but I don't know how to insert data.
Here's how my project is built:
grails create-app bookstore
cd bookstore
grails create-domain-class bookstore.Book
edit file grails-app/domain/bookstore/Book.groovy :
package bookstore
class Book {
String title
String author
Date publicationDate
static constraints = {
}
}
Scaffold:
grails generate-all bookstore.Book
Run the app:
grails run-app
Browse:
http://localhost:8080
From here I can insert and list items from the browser ;o)
Now I want to use the REST API !
Reading data is OK:
curl -H "Accept: application/json" -i http://localhost:8080
/book.jsonHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 14 Dec 2015 12:14:01 GMT
[{"class":"bookstore.Book","id":1,"author":"James","publicationDate":"2015-12-13T23:00:00Z","title":"Hello"}]
But when I try to insert data:
curl -i -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"title":"Test", "author":"Franck"}'
http://localhost:8080/book/create.jsonHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 14 Dec 2015 12:16:03 GMT
{"class":"bookstore.Book","id":null,"author":null,"publicationDate":null,"title":null}
And nothing is inserted.
The default create action doesn't save anything. It is used to populate the initial form when creating an instance using a browser form. You probably want to be invoking the save action instead.
This isn't really related to your question but FYI... if you are building a REST interface, you should look at RestfulController and/or the #Resource annotation.
I am building a Flask-based REST API and using Flask-JWT to handle JWT auth. I also want to use the built in roles management with Flask-Security. However, Flask-Security's #roles_required() decorator assumes I am showing a Flask view when it fails.
Here is my token endpoint (which is working as I want):
$ http POST localhost:5000/auth/token username='test' password='test'
HTTP/1.0 200 OK
Content-Length: 192
Content-Type: application/json
Date: Sun, 08 Nov 2015 17:45:46 GMT
Server: Werkzeug/0.10.4 Python/3.5.0
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ"
}
Here is a successful response to a resource that does not have any role requirement (using only #jwt_required) This is also working as I want:
$http GET localhost:5000/protected Authorization:'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ'
HTTP/1.0 200 OK
Content-Length: 25
Content-Type: text/html; charset=utf-8
Date: Sun, 08 Nov 2015 17:46:24 GMT
Server: Werkzeug/0.10.4 Python/3.5.0
<models.User[email=test]>
When I do the same for a resource that has roles required (such as admin in this example), it seems to assume I have a page to display such as /login which I do not since it is a headless REST API:
$ http GET localhost:5000/admin Authorization:'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ'
HTTP/1.0 302 FOUND
Content-Length: 209
Content-Type: text/html; charset=utf-8
Date: Sun, 08 Nov 2015 17:46:43 GMT
Location: http://localhost:5000/
Server: Werkzeug/0.10.4 Python/3.5.0
Set-Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsiZXJyb3IiLCJZb3UgZG8gbm90IGhhdmUgcGVybWlzc2lvbiB0byB2aWV3IHRoaXMgcmVzb3VyY2UuIl19XX0.CSEcAw.pjwXLeSWUsORXR-OU5AfFvq6ESg; HttpOnly; Path=/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: /. If not click the link.
I know Flask-Security uses Flask-Principal behind the scene for its roles management (#roles_required, etc.) and it ties into the RoleMixin and UserMixin for the datastore which is very nice. However, if there is no way to get Flask-Security to just allow the resource through without using my JWT header, then maybe the best bet is to build my own decorators which uses Flask-Principal to manage the roles.
Does anyone have any experience with this? The idea is that the entire front end can and will be built in whatever language we need and that means it may not be Flask's templates/views which is what Flask-Security appears to be doing.
Thank you for any insight anyone can provide!
Instead of a redirect, you would want to respond with an HTTP status code 403.
Your best bet is indeed to create your own decorator to manage the roles, and move away from using Flask-Security entirely.
The author of Flask-Security has mentioned that there are better ways to secure APIs, and it makes even more sense as the library is not maintained.
Flask-JWT or Flask-JWT-Extended are perfect candidates for this task. The former would require a bit more boilerplate to get things going. There is a stale PR suggesting an API to support roles, that you could use to create your own decorator if you decide to go with Flask-JWT.
The Flask-JWT-Extended docs suggest a simpler solution that might fit your case. You should follow the custom decorators section of the documentation for the full example, but here's the decorator in the nutshell:
from functools import wraps
from flask import jsonify
from flask_jwt_extended import (
verify_jwt_in_request, get_jwt_claims
)
def admin_required(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
verify_jwt_in_request()
claims = get_jwt_claims()
if claims['roles'] != 'admin':
return jsonify(msg='Admins only!'), 403
else:
return fn(*args, **kwargs)
return wrapper
This code looks for a roles claim in the JWT, and returns a 403 response if it is not admin.
When trying to POST data to my tastypie api, I always get error code 401, even when using the Django admin account for authentication and authorization. My Django server is running as development server through "python manage.py runserver IP"
I'm using the latest master-branch of tastypie and Django 1.4.3
here are parts of my api.py
class QueueResource(ModelResource):
class Meta:
queryset = Queue.objects.all()
resource_name = 'queue'
]
my curl command:
curl --dump-header -H "Content-Type: application/json" -X POST --data '{"qid": "4", "msg": "An error occured!"}' http://IP/api/fail/
the corresponding error message:
HTTP/1.0 401 UNAUTHORIZED
Date: Wed, 13 Feb 2013 19:16:00 GMT
Server: WSGIServer/0.1 Python/2.7.3
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=utf-8
Does anyone of you has an idea why I'm getting this error code?
You should define authorization that you will use in the meta class of the resource.
Default one is ReadOnlyAuthorization.
More info you can find Here
Like what UnLiMiTeD said, you need to define Authorization.
from tastypie.authorization import Authorization
class QueueResource(ModelResource):
class Meta:
authorization = Authorization()
queryset = Queue.objects.all()
resource_name = 'queue'
This will make the QueueResource have no authorization restrictions. You can post to it without verifying any credentials.
The default authorization method is a ReadOnlyAuthorization.