New to java programming and still learning. I've built a RESTful service and I'm trying to pass in a parameter for a GET routine and I'm getting back a state 400 saying that the "Request entity cannot be empty". When I call the non-parameterized GET, the data comes back just fine. I've stripped down all the functionality of the parameterized GET to just return a simple string and I'm still getting the same message. Searched all over and can't find anything that's very helpful.
Below is the code that I'm running for the service. The method "GetChildAllInfo" makes a call to a local mySQL instance and returns a list of objects; that one works just fine. The parameterized one returns nothing, not even an exception.
Any help would be tremendously appreciated. Even if it's a ridiculously simple solution like a syntax error that I may have missed. AND I'm willing to accept any other advice on what you see in the code as well. Thanks!
package allowanceManagerChild;
import java.util.Set;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.core.MediaType;
import com.google.gson.Gson;
#Path("allowanceManagerChild")
public class AllowanceManagerChild {
#Context
private UriInfo context;
/**
* Creates a new instance of AllowanceManagerChild
*/
public AllowanceManagerChild() {
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public String getJson() {
String response = "";
Set<Child> children = Child.GetAllChildInfo();
for (Child child : children){
Gson gson = new Gson();
String json = gson.toJson(child);
response = response + json;
}
return response;
}
#GET
#Path("/{childID}")
#Produces(MediaType.APPLICATION_JSON)
public String getJson(int childID) {
String response = "";
try{
// Set<Child> children = Child.GetChildInfo(id);
// for (Child child : children){
// Gson gson = new Gson();
// String json = gson.toJson(child);
// response = response + json;
// }
response = "Made it here"; //Integer.toString(childID);
}
catch(Exception e){
response = e.toString();
}
return response;
}
/**
* PUT method for updating or creating an instance of AllowanceManagerChild
* #param content representation for the resource
*/
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public void putJson(String content) {
}
}
Adding the #PathParam annotation to the method parameter might help:
#GET
#Path("/{childID}")
#Produces(MediaType.APPLICATION_JSON)
public String getJson(#PathParam("childID") int childID) {
See the RESTful Web Services Developer's Guide for more details.
Related
Just for learn, I'm trying understand a better way to create a JSON response. I expect the following output on my request:
HTTP/1.1 200 OK
Content-Length: 15
Content-Type: application/json
connection: close
{
"status": "ok"
}
Using Java for this task (In this case Quarkus), I have the follow code:
package br.com.bb.feedback.Controller;
import javax.ws.rs.Path;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.GET;
import javax.ws.rs.core.Response;
import javax.json.JsonObject;
import javax.json.Json;
#Path("/health-check")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class HealthCheckController {
#GET
public Response check() {
JsonObject health = Json.createObjectBuilder().add("status", "ok").build();
return Response.status(Response.Status.OK).entity(health).build();
}
}
So, to build the entity object I use Json.createObjectBuilder(). Have a better way to do this?
"Better" way is a matter of opinion, but there are easy to use alternate ways:
Using a Map. In Java 9+, that's really easy:
#GET
public Response check() {
return Response.status(Response.Status.OK)
.entity(Map.of("status", "ok"))
.build();
}
In Java 8 or earlier, it takes a bit extra:
#GET
public Response check() {
Map<String, String> health = new HashMap<>();
health.put("status", "ok");
return Response.status(Response.Status.OK).entity(health).build();
}
Using a POJO:
#GET
public Response check() {
return Response.status(Response.Status.OK)
.entity(new Health("ok"))
.build();
}
static final class Health {
private String status;
// constructors, getters, and setters here
}
This is my first time writing code in Java I m running in to JsonParseException when I send invalid json data in my request body. I know I have to catch this error at the point where json body is getting parsed I don't have any idea how it works in my code. It would be great if someone can explain me how to catch the error and send the 400 response instead of the 500 which is being thrown as Uncaught server error and also how the request body is getting parsed.
I m using JAXRS: 2.0.1 and jackson for Json parsing it seems. I also added an exceptionMapper to handle these exceptions but it doesn't seem to work.
//./src/main/java/com.test.rest/Routes.java
package.com.test.rest;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
public class Routes implements Xyz{
#POST
#Path("test")
#Consumes({MediaType.APPLICATION_JSON})
#Produces(MediaType.APPLICATION_JSON)
public Response testJson(#Context HttpHeaders headers, #HeaderParam("abc") String abc, TestRequest request){
if(abc == null){
return Response.status(Response.Status.BAD_REQUEST).entity("Invalid headers").build();
}else{
return Response.status(Response.Status.OK).entity(request.gettestData()).build();
}
}
}
./src/main/java/com.test.rest/TestRequest.java
package.com.test.rest;
public class TestRequest {
private String testData;
public TestRequest () {
}
public TestRequest(String testData){
setTestData(testData);
}
public String gettestData(){
return testData;
}
public void setTestData(String testData){
if(testData!=null){
testData = testData.toLowerCase();
}
this.testData =testData;
}
}
./src/main/java/com.test.rest/JsonParseExceptionMapper.java
package.com.test.rest;
import com.fasterxml.jackson.core.JsonParseException;
import javax.annotation.Priority;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.core.*;
#Provider
#Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
#Override
public Response toResponse(final JsonParseException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Cannot parse JSON")
.type(MediaType.TEXT_PLAIN)
.build();
}
}
All the files are in same level and I m using gradle to build my code
this is my request body
{
"testData":
}
//com.fasterxml.jackson.core.JsonParseException: Unexpected character
I am trying to put my error message inside the body of an WebApplicationException.
I get the status code in postman but the body remains empty.
I have tried to add a mapper like this
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
public class UnexpectedExceptionMapper implements ExceptionMapper<Exception> {
private static final transient ObjectMapper MAPPER = new ObjectMapper();
#Override
public Response toResponse(final Exception exception) {
ResponseBuilder builder = Response.status(Status.INTERNAL_SERVER_ERROR)
.entity(defaultJSON(exception))
.type(MediaType.APPLICATION_JSON);
return builder.build();
}
private String defaultJSON(final Exception exception) {
ErrorInfo errorInfo = new ErrorInfo(exception.getMessage(), exception.getMessage());
try {
return MAPPER.writeValueAsString(errorInfo);
} catch (JsonProcessingException e) {
return "{\"message\":\"An internal error occurred\"}";
}
}
}
But postman still tells me the body remains empty when I throw the error.
throw new Exception("test")
Is there a better way and why isn't this working?
The status gets updated but no body is passed with info in it.
The reason I need this is because I want to pass along the error that I get from another endpoint that I am calling.
EDIT:
I am also trying a simple Response version
but when i throw an error the entity is ignored as well
Response.status(Response.Status.UNAUTHORIZED).entity("Does not work").build();
Response.status(Response.Status.SEE_OTHER).entity("Does work").build();
I am throwing it like this for testing atm.
#POST
#Path("/place/actionid/{nr}")
#Produces("application/json")
public Response getActionIdForPlaceAssetOrder(#PathParam("nr") #Valid String nr, #Valid Body body) throws Exception {
Response response = Response.status(Response.Status.UNAUTHORIZED).entity("Some message here").build();
return response;
The above code throws the status but ignores the message. like i said any error message gets ignored. Is this maybe a jetty issue?
At the moment i would just be happy if i can get any message passed to postman.
How to make this POST request accept payload , where I pass an array of JSON objects. Is there any method to resolve this problem. Any suggestion would help ?
package com.fyle.app.resources.lambda;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.schibsted.spt.data.jslt.Parser;
import com.schibsted.spt.data.jslt.Expression;
import com.schibsted.spt.data.jslt.impl.NodeUtils;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/api")
public class LambdaFunction {
/**
* Transform the incoming JSON with JSLT and return the result.
*/
#POST
#Path("/json-post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String invoke(InputJson json) {
try {
// this must be:
// {"json" : ..., "jslt" : jslt}
JsonNode input = mapper.convertValue(json.getJson(), JsonNode.class);
// now we can do the thing
JsonNode source = NodeUtils.mapper.readTree(input.get("json").asText());
//String jslt = input.get("jstlTemplateString").asText();
String jstlTemplateString = json.get("jstlTemplateString");
Expression template = Parser.compileString(jstlTemplateString);
JsonNode output = template.apply(source);
return NodeUtils.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(output);
} catch (Throwable e) {
return "ERROR: " + e;
}
}
The JSON Payload it is accepting ::
{
"json":"[{\"Key1\": \"Value1\", \"Key2\": \"Value2\", \"Key3\": \"Value3\"},{\"Key1\":
\"Value1\", \"Key2\": \"Value2\", \"Key3\": \"Value3\"},{\"Key1\": \"Value1\", \"Key2\":
\"Value2\", \"Key3\": \"Value3\"}]ā,
"jstl":"\n[\nfor (.)\n{\n \"Key-Change-1\" : .Key1,\n \"Key-Change-2\": .\"Key2\",\n \"Key-
Change-3\" : .\"Key3\"\n}\n]\nā
}
Can we take data only in JSON format without those " \ and \n "
{"json": "["key":"value" , "key":"value" , "key":"value"]" , "jslt":"template.jslt"}
My payload class ::
public class InputJson {
Object json;
String jstlTemplateString;
public Object getJson() {
return json;
}
public void setJson(Object json) {
this.json = json;
}
public String getJstlTemplateString() {
return jstlTemplateString;
}
public void setJstlTemplateString(String jstlTemplateString) {
this.jstlTemplateString = jstlTemplateString;
}
public String get(String jstlTemplateString) {
return jstlTemplateString;
}
}
This is the class file I'm using but still have the same error.
I think the problem in with this line
JsonNode source = NodeUtils.mapper.readTree(input.get("json").asText());
This is expecting the input to be a string instead of JSON objects , Is this the root cause for this problem ?
Your "jstl" key is incorrect. Its value is "[for (.){". Then it is expecting ',' before Key-Change-1 begins. Hence the given error. You can use any online json editor to check if your json is correct or not ex. https://jsoneditoronline.org/. If your whole string is value; you have to escape the double quotes part of value using . like {
"jstl":"[for (.){\"Key-Change-1\" : .Key1,\"Key-Change-2\": .\"Key2\",\"Key-Change-3\": .\"Key3\"}]"
}
Edit:
class Payload{
MyJson json;
Template template;
}
class MyJson{
String k1;
String k2;
.....
}
class Template{
String t1;
......
}
Define constructors, getters and setters for these classes and then you can pass Payload object with your POST request.
Your method accepts json, not text. You should create a class and pass it. So given the Rahul Agrawal's answer you need to change your code to:
#POST
#Path("/json-post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.TEXT/PLAIN)
public String invoke(Payload json) {
try {
return NodeUtils.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(payload);
} catch (Throwable e) {
return "ERROR: " + e;
}
}
Also note that you return String, not json. So I changed the return type. If you want to return json, just return payload.
Make sure that your jackson providers are registered. I'm almost sure it's already happen in dropwizard, but still worth checking.
I am trying to create a REST service which will take in JSON as input in a POST method. The service will then store this in a DB and return response. I created a class called jsonFormat as attempted in this question. The code for this class -
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* #author Aj
* This class forms the format of the JSON request which will be recieved from the App
*/
#XmlRootElement
public class JsonFormat {
public double longitude;
public double latitude;
public long IMSI;
public JsonFormat(){}
public JsonFormat(double longitude,double latitude, long IMSI){
this.longitude = longitude;
this.latitude = latitude;
this.IMSI = IMSI;
}
}
However, I am still getting the unsupported media type HTTP 415 response.
I am testing by using the POSTMAN add on for chrome.
Here is my code for the service implementation -
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.POST;
import org.json.simple.JSONObject;
/**
* REST Web Service
*
* #author Aj
*/
#Path("Offers")
public class OffersResource {
#Context
private UriInfo context;
/**
* Creates a new instance of OffersResource
*/
public OffersResource() {
}
#Path("/storeMovement")
#POST
#Consumes("application/json")
#Produces("application/json")
public String storeTrace(JsonFormat jsonObj) {
JSONObject response = new JSONObject();
String ret = "";
try {
RecordMovement re = new RecordMovement(jsonObj.longitude, jsonObj.latitude, jsonObj.IMSI);
ret = re.Store();
// Clear object
re = null;
System.gc();
response.put("status", ret);
} catch (Exception e) {
response.put("status", "fail");
}
return response.toJSONString();
}
/**
* PUT method for updating or creating an instance of OffersResource
*
* #param content representation for the resource
* #return an HTTP response with content of the updated or created resource.
*/
#PUT
#Consumes("application/json")
public void putJson(String content) {
}
}
The JSON which I'm passing is -
{"longitude": "77.681307",
"latitude": "12.8250278",
"IMSI": "404490585029957"}
While submitting the request I made sure to set the type to POST and the URL is correct (http://localhost:8080/Offers/webresources/Offers/storeMovement).
Can somebody please take a look and advise what I'm doing wrong? I have gone through multiple sites where the error is mainly due to not setting the content type but this is clearly not the case here!
Solved.
I changed from using a model to a string variable. I then use the JSONParser to parse the json String received as parameter followed by type casting it to a JSONObject. Here is my modified code -
#Path("/storeMovement")
#POST
#Consumes("application/json")
#Produces("application/json")
public String storeTrace(String json) {
JSONObject response = new JSONObject();
JSONParser parser = new JSONParser();
String ret = "";
try {
Object obj = parser.parse(json);
JSONObject jsonObj = (JSONObject) obj;
RecordMovement re = new RecordMovement((double) jsonObj.get("longitude"), (double) jsonObj.get("latitude"), (long) jsonObj.get("IMSI"));
ret = re.Store();
// Clear object
re = null;
System.gc();
response.put("status", ret);
} catch (Exception e) {
response.put("status", "fail " + e.toString());
}
return response.toJSONString();
}