How to handle `jsonParseException` from Jackson parser in Java - java

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

Related

Looking for a alternative to create a JSON object instead using Json.createObjectBuilder

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
}

Return error in JSON body from webapplicationexception

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 do I make my POST request accept my JSON payload in the following format ? I am using Jackson for this operation

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.

Receiving "Request Entity Cannot be Empty" from paramaterized RESTful GET operation

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.

Android: RESTfull API called from `retrofit 2` says `Method Not Allowed`

I have developed a web service using Java and Jersey. Now I am trying to connect into it, call its methods and get data, using android.
Below is the related part of the web service.
import bean.PatientBean;
import bean.UserBean;
import db.PatientImpl;
import db.PatientInterface;
import db.UserImpl;
import db.UserInterface;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/patient")
public class PatientJSONService
{
#POST
#Path("/getPatientById/{Id}")
#Produces(MediaType.APPLICATION_JSON)
public PatientBean getPatientById(#PathParam("Id")String Id)
{
PatientInterface patinetInterface=new PatientImpl();
PatientBean patientById = patinetInterface.getPatientById(Id);
return patientById;
}
}
In my android application, I am using Retrofit 2 to call the above REST method.
private void restCall()
{
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
YourEndpoints request = retrofit.create(YourEndpoints.class);
Call<PatientBean> yourResult = request.getPatientById("ERTA001");
yourResult.enqueue(new Callback<PatientBean>() {
#Override
public void onResponse(Call<PatientBean> call, Response<PatientBean> response) {
try {
// Log.d("MainActivity", "RESPONSE_A: " + response.body().toString());
Log.d("MainActivity", "RESPONSE: " + response.errorBody().string());
}
catch(Exception e)
{
e.printStackTrace();
}
}
#Override
public void onFailure(Call<PatientBean> call, Throwable t) {
try {
t.printStackTrace();
Log.d("MainActivity", "RESPONSE: "+"FAILED");
}
catch(Exception e)
{
e.printStackTrace();
}
}
});
}
Below is my EndPoint interface
public interface YourEndpoints {
#POST("patient/getPatientById/{Id}")
Call<PatientBean>getPatientById(#Body String Id);
}
However When I run the code, I get a HTML response from Apache Tomcat Server, which basically says HTTP Status 405 - Method Not Allowed.
How can I solve this issue?
Change your ws endpoint to #GET, and then change your rest client to below code:
#GET("patient/getPatientById/{Id}")
Call<PatientBean>getPatientById(#Path("Id") String Id);
GET should be used to retrieve data from the server.
POST should be used to send data to the server.
If you are using GSON along with RetroFit, you should not need your own implementation within getPatientById(). And, yes you should be using a GET method.
public interface PatientService {
#GET("patient/getPatientById/{Id}")
Call<PatientBean> getPatientById(#Path("Id") String id);
}
If your PatientBean is setup correctly, you should be able to call the following to get a fully formed instance of the class:
PatientService service = retrofit.create(PatientService.class);
Call<PatientBean> call = service.getPatientById("ERTA001");
call.enqueue(new Callback<PatientBean> {
#Override
public void onResponse(Call<PatientBean> call, Response<PatientBean> response) {
mPatientBean = response.body();
}
#Override
public void onFailure(Call<PatientBean> call, Throwable throwable) {
throwable.printStackTrace();
}
});

Categories

Resources