Unsupported Media Type - REST Service - java

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();
}

Related

How to get TWILIO CALL info using Java Rest Client

Having next code, which use RestEasy to get to a Twilio CALL info:
import java.util.Base64;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import com.twilio.rest.api.v2010.account.Call;
public class RestGetCallInfo1 {
public static void main(String[] args) {
try {
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget = client.target("https://api.twilio.com/2010-04-01/Accounts/AC99999999/Calls/CA77777777777.json");
String credentials = "AC99999999:888888888";
String base64encoded = Base64.getEncoder().encodeToString(credentials.getBytes());
Response response = target.request().header(HttpHeaders.AUTHORIZATION, "Basic " + base64encoded).get();
int status = response.getStatus();
if (status == 200) { //OK
Call call = response.readEntity(Call.class); //<------------- This fails!
System.out.println(call);
}
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
}
I want to ask you:
What 'Rest' libraries/tools does twilio-7.47.2-jar-with-dependencies.jar use inside (in order to use that instead of RestEasy)?
How can I get the JSON call object properly? with the actual code I get:
javax.ws.rs.ProcessingException: Unable to find a MessageBodyReader of content-type application/json and type class com.twilio.rest.api.v2010.account.Call
EDIT: I am able to get the Call info in JSon format with:
String call = response.readEntity(String.class);

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.

How to handle CORS using JAX-RS with Resteasy, Angular and Wildfly10

I have a resteasy webservice on a wildfly 10 on the same machine as my angular client.
Get request works
Put and delete are called 2 times as I understood is because a preflight request is done. A curl -X DELETE http://localhost:8080/resteasyWebServices-1.0-SNAPSHOT/company/57 -i works well on it without doing requests twice. At the contrary, when called with angular client, the restWebService is called twice!
I tried to add a corsFilter but more than helping me being able to do get requests it didn't help me to solve my problem
package com.solarity.app; // {{ groupId}}.app
import com.solarity.rest.CompanyRestService;
import com.solarity.rest.PersonRestService;
import org.jboss.resteasy.plugins.interceptors.CorsFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
#ApplicationPath("/")
public class InitApplication extends Application {
/**
*
*/
Set<Object> singletons;
HashSet<Class<?>> webServiceClasses;
public InitApplication() {
super();
webServiceClasses = new HashSet<>();
webServiceClasses.add(PersonRestService.class);
webServiceClasses.add(CompanyRestService.class);
singletons = new LinkedHashSet<>();
singletons.add(this.getCorsFilter());
}
#Override
public Set<Class<?>> getClasses() {
return webServiceClasses;
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
private CorsFilter getCorsFilter() {
CorsFilter result = new CorsFilter();
result.getAllowedOrigins().add("http://localhost:4200");
return result;
}
}
I tried to implement an options method into my webservice without success...
package com.solarity.rest; // Note your package will be {{ groupId }}.rest
import com.solarity.entities.CompanyEntity;
import com.solarity.entities.PersonEntity;
import com.solarity.service.CompanyService;
import com.solarity.service.PersonService;
import com.solarity.util.ResponseUtil;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
*
*
*/
#Path("/company")
public class CompanyRestService {
protected Logger logger = LoggerFactory.getLogger(getClass());
private CompanyService companyService = new CompanyService();
#GET // This annotation indicates GET request
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public Response getAll() {
Object response = null;
String errMsg = null;
int responseStatus = -1;
try {
this.logger.debug("companyServcfindAll----------------debug");
this.logger.warn("companyServcfindAll----------------WARN");
response = companyService.findAll();
} catch (Exception e) {
errMsg = "Error getting all persons";
logger.error(errMsg, e);
}
return ResponseUtil.getAlteredResponse(response, errMsg, responseStatus, HttpMethod.GET);
}
/**
* curl -X DELETE http://localhost:8080/resteasyWebServices-1.0-SNAPSHOT/company/57 -i
*
* #param id
* #return
*/
#DELETE
#Path("/{param}")
public Response delete(#PathParam("param") Integer id){
Object response = null;
String errMsg = null;
int responseStatus = -1;
try {
logger.debug("Deleting entity", id);
companyService.delete(id);
responseStatus = HttpStatus.SC_OK;
} catch (Exception e) {
errMsg = "Error Deleting Entity:" + id;
logger.error(errMsg, e);
response = errMsg;
responseStatus = HttpStatus.SC_METHOD_FAILURE;
}
return ResponseUtil.getAlteredResponse(response, errMsg, responseStatus, HttpMethod.DELETE);
}
/**
* Not working
* #return
*/
#OPTIONS
#Path("{path : .*}")
public Response options() {
return Response.ok("")
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600")
.build();
}
}//end Class
Here is my ResponseUtils class
package com.solarity.util;
import org.apache.http.HttpStatus;
import javax.ws.rs.core.Response;
public class ResponseUtil {
/**
*
Built to counter a Angular cross-reference problem
Adapted for Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/dlssResteasy1-1.0-SNAPSHOT/person/getPersonsAsJSON. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
source answer https://stackoverflow.com/questions/23450494/how-to-enable-cross-domain-requests-on-jax-rs-web-services?answertab=votes#tab-top
More Documentation about CORS on https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
* #param param the object to send if errorMsg is null
* #param errorMsg if not null sends an error code with error Message
* #param responseStatus response status which can be found from HttpStatus.* (if <= 0 will be taken from errorMsg, or ok)
* #return an altered response which is customized
*/
public static Response getAlteredResponse( Object param, String errorMsg, int responseStatus, String httpMethod ) {
Response result = null;
int rStatus = responseStatus;
if (errorMsg != null && responseStatus <= 0) {
rStatus = HttpStatus.SC_UNPROCESSABLE_ENTITY;
} else if (errorMsg == null && responseStatus <= 0){
rStatus = HttpStatus.SC_OK;
}
if ( errorMsg == null ) {
result = Response
.status(rStatus)
.entity(param)
.build();
}else{
result = Response.status(rStatus)
.entity(errorMsg)
.build();
}
return result;
}
}
Here is the result of debug into FF
Observe the OPTIONS request and response and be certain that the OPTION response coming from the server has the correct information. It tells the client what the server is accepting
Observe later the real request PUT GET POST , etc going to the server .Does it has all the headers that you want ?
You do not need to create and OPTIONS route .See here the RFC https://www.w3.org/TR/cors/
Add to this thread the copy of the OPTIONS request and response ( not the one you created but the one from the package your are using, if you are not using a package , look for one) , to see what is wrong with the configuration.
Add also the next POST,GET,PUT, etc. both the request and response
First of all there is a documentation about CORS I HAD TO READ to understand, I couldn't avoid that as I hoped...
Two calls from Angular
Part of the answer of my problem was in fact two calls from Angular.
I didn't understand that everytime a call to subscribe on an httpclient.put() is done a call is done!
HttpClient Documentation
Calling the subscribe() method executes the observable, which is what
initiates the DELETE request.
So What I did was:
Call methodResult = httpclient.put('someUrl', someData, someHeader).subscribe({ data => { console.log('added') });
On the caller of this method call again with abovePutMethod.subscribe( data => { doSomeThingWithComponentRefresh })
So doing only ONE call to subscribe solved my twice call problem
For the rest of the CORS Protocol
Angular Client
//UrlHelper
public static putHttpRequestOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
//Function call somewhere
const result = this.httpClient.put(url, jsonStringValues, UrlHelper.putHttpRequestOptions);
Java Resteasy server
// InitApplication extends Application
public InitApplication() {
super();
webServiceClasses = new HashSet<>();
webServiceClasses.add(PersonRestService.class);
webServiceClasses.add(CompanyRestService.class);
singletons = new LinkedHashSet<>();
singletons.add(this.getCorsFilter());
}
private CorsFilter getCorsFilter() {
CorsFilter result = new CorsFilter();
result.getAllowedOrigins().add("*");
result.setAllowedMethods("OPTIONS, GET, POST, DELETE, PUT, PATCH");
result.setCorsMaxAge(86400);//Max in FF 86400=24h https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
//
return result;
}
// RestWebService
#PUT
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response put(CompanyEntity entity ){
Object response = null;
String errMsg = null;
int responseStatus = -1;
try {
logger.debug("Received entity", entity);
companyService.persist(entity);
responseStatus = HttpStatus.SC_CREATED;
} catch (Exception e) {
errMsg = "Error adding Entity:" + entity;
logger.error(errMsg, e);
response = errMsg;
responseStatus = HttpStatus.SC_METHOD_FAILURE;
}
return ResponseUtil.getAlteredResponse(response, errMsg, responseStatus, HttpMethod.PUT);
}
// Called on result of all RestWebServices (I'm sure there are better/best practices, feel free to comment me this section)
/**
* #param param the object to send if errorMsg is null
* #param errorMsg if not null sends an error code with error Message
* #param responseStatus response status which can be found from HttpStatus.* (if <= 0 will be taken from errorMsg, or ok)
* #return an altered response which is customized
*/
public static Response getAlteredResponse( Object param, String errorMsg, int responseStatus, String httpMethod ) {
Response result = null;
int rStatus = responseStatus;
if (errorMsg != null && responseStatus <= 0) {
rStatus = HttpStatus.SC_UNPROCESSABLE_ENTITY;
} else if (errorMsg == null && responseStatus <= 0){
rStatus = HttpStatus.SC_OK;
}
String accessControlAllowMethods = "GET, POST, PUT, DELETE, OPTIONS, HEAD";
if ( errorMsg == null ) {
result = Response
.status(rStatus)
.header("Access-Control-Allow-Origin", "*") //TODO: fix permission here!
.header("Access-Control-Allow-Methods", accessControlAllowMethods)
.header("Access-Control-Max-Age", "1728000")
.entity(param)
.build();
}else{
result = Response.status(rStatus)
.header("Access-Control-Allow-Origin", "*") //TODO: fix permission here!
.header("Access-Control-Allow-Methods", accessControlAllowMethods)
.header("Access-Control-Max-Age", "1728000")
.entity(errorMsg)
.build();
}
return result;
}
You can see the traffic with Chrome DevTools in the Network tab

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.

server sent events using serverside as java rest webservice

Currently working on server sent events of html5 now. I made a servlet and set the
response.setContentType("text/event-stream");
as this. Now I get the update from this servlet on my client side and my client side code is as follows:
<script >
if(typeof(EventSource)!=="undefined")
{
var url = 'http://localhost:8080/KnockOut/DateFeed.jsp';
eventSource = new EventSource(url);
eventSource.onmessage = function (event) {
var theParagraph = document.createElement('p');
theParagraph.innerHTML = event.data.toString();
document.body.appendChild(theParagraph);
}
}
else
{
document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
}
</script>
But when I change the url to call a rest full webservice written in java, it shows some error and I am note able to get the updated output.
The REST webservice code is:
#GET
public String getXml(#Context HttpHeaders header, #Context HttpServletResponse response) {
response.setHeader("cache-control", "no-cache");
response.setContentType("text/event-stream");
return "dataas: " + (new java.util.Date()).toString() + "x\n\n";
}
please help me.
Here i got the answer
import java.io.PrintWriter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.enterprise.context.RequestScoped;
import org.glassfish.jersey.media.sse.EventOutput;
import org.glassfish.jersey.media.sse.OutboundEvent;
import org.glassfish.jersey.media.sse.SseFeature;
/**
* REST Web Service
*
* #author Irshad kk
*/
#Path("WS")
#RequestScoped
public class SSEResource {
#Context
private UriInfo context;
/**
* Creates a new instance of SSEResource
*/
public SSEResource() {
}
#GET
#Produces(SseFeature.SERVER_SENT_EVENTS)
public String getServerSentEvents() {
System.out.println("haii" + System.currentTimeMillis());
return "data: " + "irshad" + System.currentTimeMillis() + "\n\n";
}
}

Categories

Resources