I am developing a Rest-API application using Java/Quarkus. My POST API accepts XML/JSON contents. I would like to identify the type of MediaType of the incoming data based on which I need to make a request to another URL by setting the appropriate content-type.
Following is the code I have so far:
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;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
#Path("/api")
public class DataGenerator {
#Path("/generate")
#POST
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces(MediaType.APPLICATION_JSON)
public String generateData(String input) throws IOException, InterruptedException {
final HttpRequest request = HttpRequest.newBuilder(URI.create("https://example.com/example"))
.header("content-type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(input))
.build();
return HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()).body();
}
}
As you can see my input can be XML/JSON. If JSON then I would like to set header("content-type", "application/json") else if the input is XML then I would like to set header("content-type", "application/xml").
Based on the content-type the URL https://example.com/example calls out a different method to generate the response.
As of now, the function is working accurately for JSON but I am unable to handle the XML Input. Can someone please let me know how can I find the incoming Input MediaType?
I have this question for spring-boot (Find the Content-type of the incoming request in Spring boot) but I am unable to understand how to do it for the Quarkus-based application? Do I need to pass again from front-end or there is some Quarkus default way?
Usually, this is set in the Content-Type header. So to pull this header you can do this (this uses JAX-RS annotation javax.ws.rs.HeaderParam):
#POST
#Produces(MediaType.TEXT_PLAIN)
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public String hello(#HeaderParam("Content-Type") String contentType, String data) {
return String.format("Data: %s%nContent-Type: %s", data, contentType);
}
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
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.
I have some problems in my application, I send a POST request, but I cannot retrieve the JsonObject in my server, this is the code to send:
String quo = "{\"network\": {\"label\": \"new net 111\",\"cidr\": \"10.20.105.0/24\"}}";
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
JsonParser json = new JsonParser();
JsonObject jo = (JsonObject)json.parse(quo);
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://localhost:7999/jersey/rest/network/"+tenant_id);
Response oj = target.request().accept(MediaType.APPLICATION_JSON)
.header("X-Auth-Token", token)
.post(Entity.json(gson.toJson(jo)));
Trying to retrieve with:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Path("/{tenant_id}")
public String createNetwork(#HeaderParam(value = "X-Auth-Token") String authToken,
#PathParam(value = "tenant_id") String tenant_id,
JsonObject network){
Response response = client.target(NOVA_ENDPOINT+tenant_id)
.request(MediaType.APPLICATION_JSON)
.header("X-Auth-Token", authToken)
.post(Entity.json(gson.toJson(network)));
System.out.println("Hello");
String responseJson = response.readEntity(String.class);
JsonObject network seems to be empty, in fact it doesn't execute the method ("Hello is not printed"), the error I get is "Invalid request body" (because the JsonObject is empty I think)..
What's wrong with my code?
Ok, I understood that the problem is related to Json handling such as I'm using Gson. This is my improved code (simplified version) following users suggestion, but I still have problems..
Client Side:
package openstack;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.URI;
public class Post {
public static HttpServer startServer() {
final ResourceConfig resourceConfig = new ResourceConfig()
.packages("openstack")
.register(GsonMessageBodyHandler.class);
return GrizzlyHttpServerFactory.createHttpServer(URI.create("http://localhost:7999/jersey/rest"), resourceConfig);
}
public static void main(String[] args) {
String quo = "{\"keypair\": {\"name\": \"MyKey\"}}";
HttpServer server = startServer();
Client client = ClientBuilder.newClient();
client.register(GsonMessageBodyHandler.class);
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
JsonParser json = new JsonParser();
JsonObject jo = (JsonObject)json.parse(quo);
WebTarget target = client.target("http://localhost:7999/jersey/rest/test/prova");
System.out.println(jo);
Response oj = target.request().post(Entity.json(jo));
String responseString = oj.readEntity(String.class);
System.out.println(responseString);
}
}
Server Side:
package openstack;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
#Path("/test")
public class Test {
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
Parliament parliament = new Parliament();
JsonParser json = new JsonParser();
private final Client client;
public Test() {
client = ClientBuilder.newClient().register(GsonMessageBodyHandler.class);
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/prova")
public Response mymethod(JsonObject keypairsob){
return Response.ok(keypairsob).build();
}
}
I created a GsonMessageBodyHandler.java in my package with the code suggested below by the user peeskillet. Added jersey-container-grizzly2-http.jar to my web-inf/lib (I don't know how to proper use Maven), but still doesn't work.. what am I missing?
In order to convert JSON to a Java type, there is need to for a MessageBodyReader and a MessageBodyWriter implementation to do the conversion to and from. Since you are using JsonObject which is a GSON type, you can see this implementation. There is a problem with the implementation though, as the readFrom method doesn't compile with Jersey 2. Here is the a fixed version
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
#Provider
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public final class GsonMessageBodyHandler implements MessageBodyWriter<Object>,
MessageBodyReader<Object> {
private static final String UTF_8 = "UTF-8";
private Gson gson;
private Gson getGson() {
if (gson == null) {
final GsonBuilder gsonBuilder = new GsonBuilder();
gson = gsonBuilder.create();
}
return gson;
}
#Override
public boolean isReadable(Class<?> type, Type genericType,
java.lang.annotation.Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public Object readFrom(Class<Object> type, Type type1, Annotation[] antns,
MediaType mt, MultivaluedMap<String, String> mm, InputStream in)
throws IOException, WebApplicationException {
InputStreamReader streamReader = new InputStreamReader(in, UTF_8);
try {
Type jsonType;
if (type.equals(type1)) {
jsonType = type;
} else {
jsonType = type1;
}
return getGson().fromJson(streamReader, jsonType);
} finally {
streamReader.close();
}
}
#Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public long getSize(Object object, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
#Override
public void writeTo(Object object, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8);
try {
Type jsonType;
if (type.equals(genericType)) {
jsonType = type;
} else {
jsonType = genericType;
}
getGson().toJson(object, jsonType, writer);
} finally {
writer.close();
}
}
}
Then we just need to register it with both the client and the application. I'm using a standalone test, where you can see the configuration here
final ResourceConfig resourceConfig = new ResourceConfig()
.packages("jersey.stackoverflow.standalone")
.register(GsonMessageBodyHandler.class);
...
Client c = ClientBuilder.newClient();
c.register(GsonMessageBodyHandler.class);
Here is the resource class I used for the test
import com.google.gson.JsonObject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import jersey.stackoverflow.standalone.provider.GsonMessageBodyHandler;
#Path("/gson")
public class GsonResource {
private final Client client;
private static final String BASE_URI = "http://localhost:8080/api/gson";
public GsonResource() {
client = ClientBuilder.newClient().register(GsonMessageBodyHandler.class);
}
#POST
#Path("/proxy")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response proxyPost(JsonObject json) {
Response response = client.target(BASE_URI)
.path("main-resource").request().post(Entity.json(json));
JsonObject fromMainResource = response.readEntity(JsonObject.class);
return Response.created(null /* should be a created URI */)
.entity(fromMainResource).build();
}
#POST
#Path("/main-resource")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response mainResource(JsonObject json) {
return Response.ok(json).build();
}
}
Here's the complete test, which requires this maven dependency
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.URI;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import static jersey.stackoverflow.standalone.Main.BASE_URI;
import jersey.stackoverflow.standalone.provider.GsonMessageBodyHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.Test;
public class GsonProviderTest {
public static HttpServer startServer() {
final ResourceConfig resourceConfig = new ResourceConfig()
.packages("jersey.stackoverflow.standalone")
.register(GsonMessageBodyHandler.class);
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), resourceConfig);
}
public static Client getClient() {
Client c = ClientBuilder.newClient();
c.register(GsonMessageBodyHandler.class);
return c;
}
#Test
public void testGetIt() {
HttpServer server = startServer();
Client c = getClient();
c.register(GsonMessageBodyHandler.class);
String quo = "{\"network\": {\"label\": \"new net 111\",\"cidr\": \"10.20.105.0/24\"}}";
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
JsonParser json = new JsonParser();
JsonObject jo = (JsonObject) json.parse(quo);
WebTarget target = c.target("http://localhost:8080/api/gson/proxy");
Response response = target.request().post(Entity.json(jo));
String responseString = response.readEntity(String.class);
System.out.println(responseString);
response.close();
c.close();
server.stop();
}
}
All the test does is send the JsonObject. Though there isn't any visible conversion to JSON, in any of my code, it is happening behind the scenes by the GsonMessageBodyHandler. If you look at the GsonResource class, you can see the methods don't do anything but send out the JsonObject. In the client test, I read the response as a String, and you can see the result the same as what sent out in the initial request.
See more about MessageBodyReaders and MessageBodyWriters
There's a simple way to get the JsonObject in com.google.gson.JsonObject type using a post request.
I am assuming that all the dependencies for com.google.gson , jersey and jax-rs are already added.
On the server side you need to have code similar to below :
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
#Path("/api")
public class JersyAPI {
private JsonParser parser= new JsonParser();
#POST
#Path("/pudding")
#Consumes("application/json")
public Response postTest(String requestBody){
Response re = Response.status(200).build();
try{
JsonObject inputObjectJson = parser.parse(requestBody).getAsJsonObject();
The code above has a rest endpoint defined with path /api/pudding and it is accepting the Request Body as String. Once you receive the Json as string on server side, com.google.gson.JsonParser can be used to convert it into the com.google.gson.JsonObject directly and this can be used in your program.
To make a request on server side you post request should look like this :
POST /rest/api/pudding HTTP/1.1
Host: localhost:8082
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: c2b087d9-4830-c8a8-2a19-78273a73898c
{
"id": 1312312,
"name": "Test",
"data": {
"test" : "data"
},
}
Have you had any JSON requests successfully parsed? It could be that you need to enable JSON support in Jersey:
https://jersey.java.net/documentation/1.18/json.html
Otherwise, it may simply be failing on your request to turn the message body into a JsonObject here:
public String createNetwork(
#HeaderParam(value = "X-Auth-Token") String authToken,
#PathParam(value = "tenant_id") String tenant_id,
JsonObject network)