I have this Jersey POST resource :
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response blablabla(InputStream inputStream,
#BeanParam ImportParams importParams) throws IOException, JAXBException, SAXException {
Here is the ImportParams class :
public class ImportParams {
#QueryParam(value = "importType")
public ImportType importType = ImportType.MERGE;
#ApiParam("Force stop point type for all stop points in file. Useful if no modality defined in the netex file.")
#QueryParam(value = "forceStopType")
public StopTypeEnumeration forceStopType;
}
When I use curl to post to the the resource, only the first query parameter I specify after the question mark in the URL is read by jersey :
curl -XPOST -H"Content-Type: application/xml" -H"authorization: bearer $TOKEN" -d#$3 http://localhost:8585/services/stop_places/netex?forceStopType=TRAM_STATION&importType=MERGE
==> forceStopType has the right value, and importType is null
curl -XPOST -H"Content-Type: application/xml" -H"authorization: bearer $TOKEN" -d#$3 http://localhost:8585/services/stop_places/netex?importType=MERGE&forceStopType=TRAM_STATION
==> importType has the right value and forceStopType is null
I've used #BeanParam many times before and it used to work, so I must be missing something obvious.... Thanks for your help
Haha found out - stupid me. Had to put double quotes around the URL when cURLing :
curl -XPOST -H"Content-Type: application/xml" -H"authorization: bearer $TOKEN" -d#$3 "http://localhost:8585/services/stop_places/netex?forceStopType=TRAM_STATION&importType=MERGE"
Related
I have a basic SpringBoot 2.0.6.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR with a restful architecture
#PutMapping(path = "/users/alarms2", consumes = "application/json", produces = "application/json")
public void setAlerts2(#RequestHeader(value = "Authorization") String authHeader,
#RequestBody AlertConfiguration alertConfiguration)
throws DataAccessException, ClientProtocolException, SQLException, IOException {
..
}
but when I call this method from curl:
curl -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJyaWNhcmQub2SAsZUBnbWFpbC5jb20iLCJleHAiOjE2MDAxODMzNDAsImlhdCI6MTUzOTcwMzM0MH0.2gbyyGnkcoHjOw7HbUBBQgb59Bw8iAyFbqTe2DPUlOA-V5UwrW3KXWHZlXssZni8oRJ_o1QRzAFtAWMfz7f0Yw" -d ‘{“symbol": “MENU”, "alarmKey”:”VEGAN” , "enabled": "true"}' "http://95.90.225.68:1133/restos/api/v1/users/alarms2"
I got this error in the server:
2018-10-17 17:16 [http-nio-1133-exec-9] WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver.resolveException(140) - Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'long'; nested exception is java.lang.NumberFormatException: For input string: "alarms2"]
You probably have another method with a more general signature that overlaps given one. Something like:
#PutMapping(path = "/users/{userId}", ...)
public void someMethod(#PathVariable(value = "userId") Long userId) {
...
}
So when you consume /api/v1/users/alarms2 Spring first tries to convert "alarms2" (which is obviously not a valid Long) to userId (which is Long)
I am trying to understand what is the reason of existence #JsonInclude to use it in my DTOs. Let's look at this simple example:
A Code:
class DemoApplication {
static void main(String[] args) {
SpringApplication.run DemoApplication, args
}
#PostMapping("/")
String greet(#RequestBody Greeting greeting) {
return "Hello ${greeting.name}, with email ${greeting.email}"
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
class Greeting {
String name
String email
}
B Code:
class DemoApplication {
static void main(String[] args) {
SpringApplication.run DemoApplication, args
}
#PostMapping("/")
String greet(#RequestBody Greeting greeting) {
return "Hello ${greeting.name}, with email ${greeting.email}"
}
}
class Greeting {
String name
String email
}
The only difference between the A code and the B code is that in the B code the greeting class does not use the #JsonInclude annotation.
But if I do some simple CURL requests to that endpoint (A code and B code):
~ curl -H "Content-Type: application/json" -X POST localhost:8080
{"timestamp":"2018-04-22T21:18:39.849+0000","status":400,"error":"Bad Request","message":"Required request body is missing: public java.lang.String com.example.demo.DemoApplication.greet(com.example.demo.Greeting)","path":"/"}
~ curl -H "Content-Type: application/json" -X POST localhost:8080 -d '{}'
Hello null, with email null
~ curl -H "Content-Type: application/json" -X POST localhost:8080 -d '{"name": "AlejoDev"}'
Hello AlejoDev, with email null
~ curl -H "Content-Type: application/json" -X POST localhost:8080 -d '{"name": "AlejoDev", "email":"info#alejodev.com"}'
Hello AlejoDev, with email info#alejodev.com
Get the same behavior, then what is the utility of using #JsonInclude annotation?
For example, I expected that in A code when I sent a request with only one field, like this:
~ curl -H "Content-Type: application/json" -X POST localhost:8080 -d '{"name": "AlejoDev"}'
the Greeting object became an object with only one field, and not an object with two fields. one full and one empty.
What that annotation means is that when an object is deserialized whether to include only non-null fields on not.
When you are using it for an object that you are getting as an input, even if the input fields are null, because the variables name and email are class level fields, they will be inited to their default value which is null.
So when you create your return statement you are always going to get null for those.
Try this annotation with a JSON object as a response and then try populating some fields and keeping some fields null and then see the response to various calls. This will help you understand the effects of this annotation.
#JsonInclude(Include.NON_NULL) or #JsonInclude(JsonInclude.Include.NON_NULL)
is used to ignore null fields in an object.
In your particular example you have returned a String value that is why it is printing null.
If you try to return the complete object, then you will find that the null fields are not included in the response body
I'm using jersey for my rest server, and I got a HTTP 405 error, when I try to forward POST request to relative GET resource.
#Path("/")
public class MyResource {
#POST
#Path("/{method}")
#Produces(MediaType.APPLICATION_JSON)
public String postRequest(#PathParam("method") String method, #Context UriInfo uriInfo, String body) throws IOException {
JsonParser parser = new JsonParser();
JsonObject root = parser.parse(body).getAsJsonObject();
JsonObject params = root;
if (root.has("method")) {
method = root.get("method").getAsString();
params = root.getAsJsonObject("params");
}
UriBuilder forwardUri = uriInfo.getBaseUriBuilder().path(method);
for (Map.Entry<String, JsonElement> kv : params.entrySet()) {
forwardUri.queryParam(kv.getKey(), kv.getValue().getAsString());
}
return new SimpleHttpClient().get(forwardUri.toString());
}
#GET
#Path("/mytest")
#Produces(MediaType.APPLICATION_JSON)
public String getTest(#QueryParam("name") String name) {
return name;
}
}
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/anythingbutmytest
curl -X GET localhost/mytest?name=jack
These two curl above work fine. But I get a 405 error , when I try to request like this:
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/mytest
javax.ws.rs.NotAllowedException: HTTP 405 Method Not Allowed
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.getMethodRouter(MethodSelectingRouter.java:466)
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.access$000(MethodSelectingRouter.java:94)
......
What should I do?
-------------------------------------Update-------------------------------------
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/mytest
This curl work fine, when I add a post method like below. But I will write a same POST method for every GET Method like that, is there any other solution?
#POST
#Path("/mytest")
#Produces(MediaType.APPLICATION_JSON)
public String postMyTest(#Context UriInfo uriInfo, String body) throws Exception {
return postRequest(uriInfo.getPath(), uriInfo, body);
}
Besides, is there any other way to re-route POST request to a method in the same class without building a new HTTP request?
You should make
#POST
#Path("/mytest")
and not "getTest" method.Reason is below.
Command
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/anythingbutmytest
will accept because of
#Path("/{method}") .
But
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/mytest
will not accept because of
#GET
#Path("/mytest")
POST does not match GET.
How do I send data (JSON or XML) and deserialize it in grails controller?
I tried render params and render request.JSON whitch returned me empty json, and also the command approach
import grails.converters.JSON
class TestController {
def test(RestCommand r){
render r as JSON
}
}
class RestCommand {
String data
static constraints = {
data nullable: false
}
}
witch resulted in
{"data":null,"errors":{"errors":[{"object":"x.x.RestCommand","field":"data","rejected-value":null,"message":"Property [data] of class [class x.x.RestCommand] cannot be null"}]}}
here's my curl request:
curl -X POST --header "Content-Type:application/json" --data '{"id":1}' localhost:8080/myApp/myController/myMethod
P.S. I have viewed similar questions in stackexchange, but as I've discussed above, none of the approaches worked.
Grails provides serialized object for you, your data just has to have the same fields as command.
This works for me:
import grails.converters.JSON
class TestController {
def test(RestCommand r){
if(r.hasErrors()) {
render (
status: 400,
text: r.getErrors() as JSON)
} else {
render "id: "+r.id+" data: "+r.someData
}
}
}
class RestCommand {
Integer id
String someData
static constraints = {
id nullable: false
}
}
Here's the request:
curl -X POST --header "Content-Type:application/json" --data '{"id":1,"someData":"here you go some data"}' localhost:8080/myapp/test/test
results:
id: 1 data: here you go some data
They way you are doing it is working (sending JSON to a Controller and deserializing it). If you send JSON to this action, the command will bind the received data (like it does in your example). Your problem is that the data in your curl test is invalid. Normally, you would check if the Command has errors like :
def test(RestCommand r){
if(r.hasErrors()) {
// handle error
response.status = 400
render "error"
return
}
render "ok"
}
I have the following endpoint declared in my JAX-RS application:
#WebService
public interface AlertWeb
{
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public StringResponse addAlert(String name,
int amount, String timespan, String repo, String action);
}
I'm using the following curl command to call this endpoint:
curl -X POST -H "Cache-Control: no-cache"
-H "Content-Type: application/x-www-form-urlencoded"
-d "name=yellow&amount=2×pan=DAY&repo=A&action=%7Baction%3A'GreenDivAction'%2C+message%3A'Simple+Message'%2C+args%3A+%5B'arg1'%2C'arg2'%5D%7D"
http://localhost:8080/AdminService/alert/add
but keep getting the following error when I make the request:
javax.ws.rs.BadRequestException: java.lang.NumberFormatException: For input string: ""
Note Line breaks in curl syntax added for readability.
What am I doing wrong?
You will need to add #FormParam to your method parameters if you want them to be injected as such
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response addAlert(
#FormParam("name") String name,
#FormParam("amount") int amount,
#FormParam("timespan") String timespan,
#FormParam("repo") String repo,
#FormParam("action") String action) {
}