I am getting a 415 Error when sending a form entry to another client resource via JSON. The target URI in my code below ("/message") works when not using the form (i.e. hit "/message" with a test mock object).
Here is my code to get the values of the form and do the post to the target resource. Am I missing something that needs to be done?
I am using the following:
Restlet: 2.1 RC5
GAE: 1.6.1
Form Restlet:
#Post
public void handlePost(Representation entity) {
final Form webForm = new Form(entity);
MessageEntity newMessage = new MessageEntity();
String subject = webForm.getFirstValue("subject");
String sendto = webForm.getFirstValue("email");
String message = webForm.getFirstValue("message");
newMessage.setCategoryID(subject);
newMessage.setAccountID(sendto);
newMessage.setMessageText(message);
ClientResource cr = new ClientResource(getRootRef()+ "/message");
cr.post(newMessage, MediaType.APPLICATION_JSON);
}
Target Resource ("/message")
#Post("json")
public void HandleRequest(MessageEntity messageEntity) {
// Logic here
}
Please let me know if you need more information
Thanks!
I have code that is very similar to yours that works fine. I am also running similar versions of Restlet and GAE. First question I have is are there other #Post methods in your Target Resource as sometimes the ordering matters.
Here are two versions of code that I have that work....
1)
public Representation postHandler() {
Reference commitsRef = new Reference(Consts.RESOURCE_BASE + "commitments/");
ClientResource commitsResource = new ClientResource(getContext(), commitsRef);
....
Representation commitsRep = commitsResource.post(commitForm);
That is posting a form to a Target resource that handles both #Post("json") and #Post("form")
2)
public Representation doPostFromGet() {
Reference takeActRef = new Reference(Consts.RESOURCE_BASE + "commitment/"
+ commitmentId + "/userActs/");
ClientResource takeActResource = new ClientResource(getContext(), takeActRef);
...
Representation takeActRep = takeActResource.post(newAct);
That is posting a Java object to a form that uses what I call the "Peierls magic". See:
http://tembrel.blogspot.com/2012/03/converting-forms-in-restlet-to-pojos.html
It allows you to have one post() in the Target and accept both forms and pojos.
On a minor note, if you are doing a post to add a new message, should the url be "/messages/" (plural) - and perhaps there is a typo somewhere? (An unlikely possibility, but I thought I would mention it).
Good luck,
RB
Related
I am looking for existing solutions to match dynamic parameters with HttpCore. What I have in mind is something similar to constraints in ruby on rails, or dynamic parameters with sails (see here for example).
My objective is to define a REST API where I could easily match requests like GET /objects/<object_id>.
To give a little bit of context, I have an application that creates an HttpServer using the following code
server = ServerBootstrap.bootstrap()
.setListenerPort(port)
.setServerInfo("MyAppServer/1.1")
.setSocketConfig(socketConfig)
.registerHandler("*", new HttpHandler(this))
.create();
And the HttpHandler class that matches the requested URI and dispatches it to the corresponding backend method:
public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) {
String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
// Parameters are ignored for the example
String path = request.getRequestLine().getUri();
if(method.equals("POST") && path.equals("/object/add") {
if(request instanceof HttpEntityEnclosingRequest) {
addObject(((HttpEntityEnclosingRequest)request).getEntity())
}
[...]
For sure I can replace path.equals("/object/add") by something more sophisticated with RegEx to match these dynamic parameters, but before doing so I'd like to know if I am not reinventing the wheel, or if there is an existing lib/class I didn't see in the docs that could help me.
Using HttpCore is a requirement (it is already integrated in the application I am working on), I know some other libraries provide high-level routing mechanisms that support these dynamic parameters, but I can't really afford switching the entire server code to another library.
I am currently using httpcore 4.4.10, but I can upgrade to a newer version of this might help me.
At present HttpCore does not have a fully featured request routing layer. (The reasons for that are more political than technical).
Consider using a custom HttpRequestHandlerMapper to implement your application specific request routing logic.
final HttpServer server = ServerBootstrap.bootstrap()
.setListenerPort(port)
.setServerInfo("Test/1.1")
.setSocketConfig(socketConfig)
.setSslContext(sslContext)
.setHandlerMapper(new HttpRequestHandlerMapper() {
#Override
public HttpRequestHandler lookup(HttpRequest request) {
try {
URI uri = new URI(request.getRequestLine().getUri());
String path = uri.getPath();
// do request routing based on the request path
return new HttpFileHandler(docRoot);
} catch (URISyntaxException e) {
// Provide a more reasonable error handler here
return null;
}
}
})
.setExceptionLogger(new StdErrorExceptionLogger())
.create();
I created a series of REST services in Java using Restlets. The majority of these services use JSON, and I have no problem accessing them using SOAP UI via a GET request. However, when I try to access POST based services using SOAP UI, the Representation entity parameter is always null. I have searched Stack Overflow as well as the web, but could find nothing which I either haven't already done, or which addresses my problem.
Here is the code for a POST resource which always seems to receive a null entity:
public class CreateAccountResource extends ServerResource {
#Post("json")
public Representation createAccount(Representation entity) throws IOException {
String message = null;
boolean result = true;
try {
String post = entity.getText();
Object obj = new JSONParser().parse(post);
JSONObject jsonObject = (JSONObject) obj;
String username = (String) jsonObject.get("username");
String password = (String) jsonObject.get("password");
String email = (String) jsonObject.get("email");
// more code
}
catch (Exception e) {
// handle exception here
}
}
}
And here is a screen shot from my SOAP UI showing the configuration I used when sending the request:
In case you are wondering, I am using IntelliJ in debug mode to inspect the value of the entity, and the project uses Maven.
I never use Restlet however I think that since you specify #Post("json") annotation for your createAccount method; the method is waiting for a json in the POST body instead of passing the values as a query parameters.
So probably you must change your actual POST with the query parameters to a POST call to your URL http://localhost:8080/MyApp/service/createAccount passing the parameters in the body as json:
{
"username" : "tim",
"password" : "password",
"email" : "tim#me.com"
}
In SOAPUI could be something like:
Hope it helps,
I'm using Google Http Client and Jackson to query data to backend (JSON API).
I need to pass parameters (one Java bean object). The object might have few or lot of field. Initially I attempt to pass it as content as follow:
HttpRequest request = requestFactory.buildGetRequest(getUrl(api)).setContent(new JsonCContent(jsonFactory, params));
However, I'm not allowed to set the HTTP content in GET operation.
Any suggestion how can I pass these parameters?
Under one condition:
I don't want to write a util method to convert this object into string of URL parameters. But if there's already reusable API to do it, that would be fine.
I need generic solution if possible. Because I'm going to apply this to 600 JSON API calls.
My last alternative would be to change backend to expect POST request instead of GET, then I perform POST operation on the client side.
Thanks
Instead of extends GenericUrl, you can use GenericUrl.put (inherit from GenericData) to set query parameters. For example:
GenericUrl genericUrl = new GenericUrl("http://yourapi.com/request");
genericUrl.put("user", "user name");
genericUrl.put("token", "token values");
HttpRequest request = requestFactory.buildGetRequest(genericUrl);
It seems like the expected usage is to extend the URL class you are using for your buildGetRequest() call. For instance, let's say you wanted to provide two extra query parameters called "user" and "token". You could do this with the following:
HttpRequest request = requestFactory.buildGetRequest(
new CustomUrl("http://www.yourserver.com").setUser(userId).setToken(token));
where the CustomUrl class is defined as:
public class CustomUrl extends GenericUrl {
public CustomUrl(String encodedUrl) {
super(encodedUrl);
}
#Key("user")
private String mUserId;
#Key("token")
private String mToken;
public CustomUrl setUser(String userId) {
mUserId = userId;
return this;
}
public CustomUrl setToken(String token) {
mToken = token;
return this;
}
}
The values are not necessary for the #Key annotations, but will be used as the name of the respective query parameters if provided. If omitted, the name of the variable will be used instead (see example)
Check google-http-client's javadoc for more info.
I know there are a bunch of questions on this already - I'm having a hard time sorting out which ones are related to problems with versions, and which ones are related to jQuery (which I'm not using), etc. I have the MVC4 RC (4.0.20505.0), Visual Studio 2010 Ultimate SP1.
I have a complex type in my model:
public HttpResponseMessage Post([FromUri]Person person)
{
TableStorageHelper personHelper = new TableStorageHelper();
personHelper.Save(personHelper.GetTableNameForType("Person"), person);
var response = Request.CreateResponse<Person>(HttpStatusCode.Created, person);
return response;
}
I am passing in this JSON string - using Fiddler mostly, but also trying from code in another controller (trying to do all testing locally just to verify that I can get values in the object received by the controller):
The JSON:
{"FirstName":"Andy","LastName":"Schultz","PartitionKey":"USW","RowKey":"per-928c8f74-2efd-4fc2-a71c-fb3ea8acc6d7","NickName":null,"FullName":"Andy Schultz","Description":null,"ImageLocation":null,"Region":"USW","CommentsAboutMe":{"Comments":[]},"CommentsByMe":{"Comments":[]}}
All of the properties here do exist in the class.
The code from the other controller:
HttpWebRequest request = HttpWebRequest.Create("http://127.0.0.2:8080/api/persons/") as HttpWebRequest;
request.Method = "POST";
request.ContentType = "text/json";
using (var writer = new StreamWriter(request.GetRequestStream()))
{
Person person = new Person("Andy", "Schultz", "USW");
Formatting formatting = new Formatting();
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
string json = JsonConvert.SerializeObject(person, formatting, settings);
writer.Write(json);
}
Every time, I get an empty Person object in my controller (I'm debugging on the very first line there). Every value is NULL.
You may have noticed the [FromUri] attribute on my controller - I had to do that to get around an error telling me there was no ModelBinder for an undefined type, but I'm not sure that it's correct - I'm not passing any info in the Uri, it's in the body, as you can tell.
Thank you Mike Stall: http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx
The [FromUri] attribute did indeed tell my controller to read the uri and not the request body looking for the parameter for my controller method. It wasn't there, so everything was null.
The error that adding that attribute fixed, which said there was no formatter defined for a type of content Undefined, was caused by my improperly declaring the content-type of the request. The correct way was "Content-Type: text/json; charset=utf-8
I have a rest webservice (with jersey) which returns json list, if i call it directly it returns exactly this :
[{"success":false,"uri":"foo:22","message":"Unknown host : foo"},{"success":true,"uri":"localhost:8082","message":null}]
generated by this snippet :
#GET
#Path("/opening/")
public List<OpeningResult> testOpenings(#QueryParam("uri") List<String> uris) {
LOG.debug("testOpenings request uris :[" + uris + "]");
List<OpeningResult> openingResults = infoService.testOpenings(uris);
return openingResults;
}
It's a Collection of Pojo which look like this :
#XmlRootElement(name = "OpeningResult")
public class OpeningResult {
attributes
...
getter/setter
}
this Pojo is shared through a common jar between the server and the client.
i call the web service with this snippet :
Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/scheduler/rest/opening");
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
for (String uri : uris) {
params.add("uri", uri);
}
List<OpeningResult> results = newArrayList(resource.queryParams(params).get(OpeningResult[].class));
I add some trace on the server side, i see that my rest service is called with the good parameters, buth on client side, i have this error :
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"success"). Expected elements are <{}OpeningResult>
I don't find where it comes from ?
Modify your code to set up your client like this:
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
Client client = Client.create(clientConfig);
I had the exact same problem until this question and its answers pointed me in the right direction.
The situation is caused by the default jersey-json module used for serialization to and from JSON, which does not handle certain JSON constructs properly.
You can set the FEATURE_POJO_MAPPING flag to use the Jackson library's JacksonJsonProvider for JSON serialization instead.
Check out the Jersey Client side doc on using JSON. It looks like you're at least missing the annotation:
#Produces("application/json")
But you could also be missing the POJO Mapping feature filters for both client and server side. These all seem to be minor configuration changes.