Using #Consume with GET request in Jersey Rest - jsonp

I'm trying to bind values in a GET request to a POJO.
The values are parameters in a HTTP GET request. I'm using JSONP to pass the parameters however it looks like JSONP pushes the JSON object up onto the Request line so its not really a JSON object which is being sent but instead just name value pairs on the URL.
Is it possible to map the values in my GET request to a POJO? Jersey gives the following exception when i try binding
A HTTP GET method, public void handleJSONP(MyPojo), should not consume any entity.
The binding code is looking in the request body however it doesnt exist because it is a GET request. Is there any other way to bind the values in the request without having to manually include a #QueryParam entry for each ?
Thanks

I was able resolve this by using #com.sun.jersey.api.core.InjectParam of jersey
public JSONWithPadding doSomething(#InjectParam final MyPojo argPojo)
Then the Pojo looks like this
public class MyPojo
{
/** */
#QueryParam("value1")
private String value1;
/** */
#QueryParam("value2")
private String value2;
/** */
#QueryParam("value3")
private List<String> value3;

HTTP GET by specification includes the parameters in the URL - therefore it only accepts value pairs. So, what you are trying to do is not feasible. why don't you use a POST instead to bundle a JSON object together with the request?

I am proposing a more expanded example.
jQuery client side:
var argPojo = {
callback:"myPojoCallback",
value1:"val1",
value2:"val2",
value3:["val1", "val2", "val3"]
};
var url = 'xxx.xx.xx.xx/testPojo';
$.ajax({
type: 'GET',
async: false,
jsonpCallback: argPojo.callback,
url: url,
data:argPojo,
contentType: "application/json",
dataType: 'jsonp',
beforeSend:function(){
console.log("sending:",argPojo);
},
success: function(response) {
console.log("reciving",response);
},
error: function(e) {
console.error("error",e);
}
});
on the server
#Path("testPojo")
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces("application/x-javascript")
public JSONWithPadding testPojo(#InjectParam MyPojo argPojo){
System.out.println(argPojo);
System.out.println(argPojo.callback);
System.out.println(argPojo.value1);
System.out.println(argPojo.value2);
System.out.println(argPojo.value3);
return new JSONWithPadding(argPojo, argPojo.callback);
}
the actual class object
public class MyPojo {
#QueryParam("callback")
public String callback;
#QueryParam("value1")
public String value1;
#QueryParam("value2")
public String value2;
#QueryParam("value3[]")
public List<String> value3;
public MyPojo(){}
}
chrome console result
sending: Object
callback: "myPojoCallback"
value1: "val1"
value2: "val2"
value3: Array[3]
__proto__: Object
receiving Object
callback: "myPojoCallback"
value1: "val1"
value2: "val2"
value3: Array[3]
__proto__: Object

As we know GET request cannot consume any entity, we need to pass each parameter as params. To be simple we can do like the below using javax.ws.rs.BeanParam
(We can use the #BeanParam instead of #InjectParam
public JSONWithPadding doSomething(#BeanParam final MyPojo argPojo)
....
public class MyPojo
{
/** */
#QueryParam("value1")
private String value1;
/** */
#QueryParam("value2")
private String value2;
/** */
#QueryParam("value3")
private List<String> value3;

GET request cannot consume any entity.
Instead, use POST or PUT methods (provided request is for insert or update).
Otherwise, go with standard way of passing attributes in URL.

Related

ASP.NET Core 2.1 API POST body is null when called using HttpWebRequest, seems it can't be parsed as JSON

I'm facing a strange bug, where .NET Core 2.1 API seems to ignore a JSON body on certain cases.
I advised many other questions (e.g this one, which itself references others), but couldn't resolve my problem.
I have something like the following API method:
[Route("api/v1/accounting")]
public class AccountingController
{ sometimes it's null
||
[HttpPost("invoice/{invoiceId}/send")] ||
public async Task<int?> SendInvoice( \/
[FromRoute] int invoiceId, [FromBody] JObject body
)
{
// ...
}
}
And the relevant configuration is:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new TestJsonConverter());
})
.AddJsonFormatters()
.AddApiExplorer();
// ...
}
Where TestJsonConverter is a simple converter I created for testing why things doesn't work as they should, and it's simple as that:
public class TestJsonConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return token;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary (would be neccesary if used for serialization)");
}
}
Calling the api method using Postman works, meaning it goes through the JSON converter's CanConvert, CanRead, ReadJson, and then routed to SendInvoice with body containing the parsed json.
However, calling the api method using HttpWebRequest (From a .NET Framework 4, if that matters) only goes through CanConvert, then routes to SendInvoice with body being null.
The request body is just a simple json, something like:
{
"customerId": 1234,
"externalId": 5678
}
When I read the body directly, I get the expected value on both cases:
using (var reader = new StreamReader(context.Request.Body))
{
var requestBody = await reader.ReadToEndAsync(); // works
var parsed = JObject.Parse(requestBody);
}
I don't see any meaningful difference between the two kinds of requests - to the left is Postman's request, to the right is the HttpWebRequest:
To be sure, the Content-Type header is set to application/json. Also, FWIW, the HttpWebRequest body is set as follows:
using(var requestStream = httpWebRequest.GetRequestStream())
{
JsonSerializer.Serialize(payload, requestStream);
}
And called with:
var response = (HttpWebResponse)request.GetResponse();
Question
Why does body is null when used with HttpWebRequest? Why does the JSON converter read methods are skipped in such cases?
The problem was in the underlying code of the serialization. So this line:
JsonSerializer.Serialize(payload, requestStream);
Was implemented using the default UTF8 property:
public void Serialize<T>(T instance, Stream stream)
{
using(var streamWriter = new StreamWriter(stream, Encoding.UTF8) // <-- Adds a BOM
using(var jsonWriter = new JsonTextWriter(streamWriter))
{
jsonSerializer.Serialize(jsonWriter, instance); // Newtonsoft.Json's JsonSerializer
}
}
The default UTF8 property adds a BOM character, as noted in the documentation:
It returns a UTF8Encoding object that provides a Unicode byte order
mark (BOM). To instantiate a UTF8 encoding that doesn't provide a BOM,
call any overload of the UTF8Encoding constructor.
It turns out that passing the BOM in a json is not allowed per the spec:
Implementations MUST NOT add a byte order mark (U+FEFF) to the
beginning of a networked-transmitted JSON text.
Hence .NET Core [FromBody] internal deserialization failed.
Lastly, as for why the following did work (see demo here):
using (var reader = new StreamReader(context.Request.Body))
{
var requestBody = await reader.ReadToEndAsync(); // works
var parsed = JObject.Parse(requestBody);
}
I'm not very sure. Certainly, StreamReader also uses UTF8 property by default (see remarks here), so it shouldn't remove the BOM, and indeed it doesn't. Per a test I did (see it here), it seems that ReadToEnd is responsible for removing the BOM.
For elaboration:
StreamWriter and UTF-8 Byte Order Marks
The Curious Case of the JSON BOM

Using Volley For Spoonacular Api

How to use Volley to fetch recipes from spoonacular API for an android application. I am new to APIs and would like some help in fetching recipes from the spoonacular api for a list of ingredients specified in an android app.
Step 1
dependencies {
...
implementation 'com.android.volley:volley:1.0.0'
}
Step 2
In AndroidManifest file add permission
<uses-permission android:name="android.permission.INTERNET"/>
Step 3 Add following in MyApplication class
private RequestQueue requestQueue;
public RequestQueue getRequestQueue() {
if (requestQueue == null)
requestQueue = Volley.newRequestQueue(getApplicationContext());
return requestQueue;
}
public void addToRequestQueue(Request request, String tag) {
request.setTag(tag);
getRequestQueue().add(request);
}
public void cancelAllRequests(String tag) {
getRequestQueue().cancelAll(tag);
}
Step 4 Final Step
//URL of the request we are sending
String url = "https://api.spoonacular.com/food/products/22347";
/*
JsonObjectRequest takes in five paramaters
Request Type - This specifies the type of the request eg: GET,
URL - This String param specifies the Request URL
JSONObject - This parameter takes in the POST parameters.null in case of
GET request
Listener -This parameter takes in a implementation of Response.Listener()
interface which is invoked if the request is successful
Listener -This parameter takes in a implemention of Error.Listener()
interface which is invoked if any error is encountered while processing
the request
*/
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET,
url, null,
new Response.Listener() {
#Override
public void onResponse(JSONObject response) {
//Success Callback
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//Failure Callback
}
});
// Adding the request to the queue along with a unique string tag
MyApplication.getInstance().addToRequestQueue(jsonObjectReq, "getRequest");
Something like that. Let's try. Thanks

JAX-RS : HTTP Status 415 - Unsupported Media Type

I got this error when making an ajax request to my WebService :
HTTP Status 415 - Unsupported Media Type
I tried to add the good MediaType (Text/Html, i think), but it doesn't work. I have still this error. What could this be, do you think ?
Thank you !
My request :
$(document).on('submit','.form-add-edit',function(e){
e.preventDefault();
var idDisruptive = $(e.target).find('input[name=idDisruptive]').val();
var url = "api/disruptive";
var method = "POST";
if (idDisruptive){
url = url + '/' + idDisruptive;
method = "PUT";
}
$.ajax({
url: url,
method : method,
data : getDisruptiveParams(),
success : function (response){
console.log('EDIT')
console.log(response);
//editDisruptive(response);
},
error : function(response){
console.log('EDIT ERROR')
console.log(response);
}
});
});
The Web Service :
#Stateless
#Path("disruptive")
#Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON})
public class DisruptiveFacadeREST extends AbstractFacade<Disruptive> {
#PersistenceContext(unitName = "StoryGeneratorPU")
private EntityManager em;
public DisruptiveFacadeREST() {
super(Disruptive.class);
}
#POST
#Override
public void create(Disruptive entity) {
super.create(entity);
}
#PUT
#Path("{id}")
public void edit(#PathParam("id") Integer id, Disruptive entity) {
super.edit(entity);
}
#Override
protected EntityManager getEntityManager() {
return em;
}
}
You need to set the content-type on the jQuery request. If you don't, it will default to application/x-www-form-urlencoded. And just because you add #Consumes(MediaType.APPLICATION_FORM_URLENCODED) doesn't mean that JAX-RS will not how to convert the form data to Disruptive. There need to be a MessageBodyReader to handle that conversion, which there isn't. Same goes for MediaType.TEXT_HTML. Just adding that means nothing if there is no converter to handle the conversion. Remove those two. What you want is to handle JSON conversion, and there should already be a MessageBodyReader included in the EE server that will convert JSON data to arbitrary POJOs.
So for the jQuery, just add
$.ajax({
contentType: 'application/json'
})
That should solve the problem.

Show XML/JSON sample value in Swagger UI using swagger's annotations

I am implementing Jersey based REST API and using swagger to generate HTML based documentation for the same. I am using swagger's annotations to read and scan the resources to generate documentation. I have specified response for each resource using #ApiResponse annotation as below:
#Path("/hello")
#Api(value = "Hello World" )
public class HelloRest
{
#GET
#ApiOperation(value="Hello world", httpMethod="GET")
#ApiResponses(value={ #ApiResponse(code = 200, message = "Success", response = WebservicesErrorResponse.class, reference = "C:/Desktop/hello.json")
#ApiResponse(code = 404, message = "Not found", response = WebservicesErrorResponse.class)})
#Produces({"application/json", "application/xml"})
public Response helloWorld()
{
return Response.status(WebservicesCommonTypes.SUCCESS).entity("Hello rest API").build();
}
}
It is working fine and it is generating HTML based documentation as below:
As it shows the complete structure (Model and example value) of response if response code is 404. And in example value, it is not showing the values, only showing the type for each parameter for the model.
I want to show the sample example schema for the response so that client can understand that what would be the exact response for each response. I researched on it and I found that there is one attribute:
#ApiResponse(reference = "") - Specifies a reference to the response type. The specified reference can be either local or remote and will be used as-is, and will override any specified response() class.
I tried it and I give it a path for my sample.json file as below:
#ApiResponse(code = 200, message = "Success", response = WebServicesErrorResponse, reference = "http://localhost:9001/myinstanceofapplication/html/api-doc/hello.json")
and I also tried to give another path that is local path like below:
#ApiResponse(code = 200, message = "Success", response = WebservicesErrorResponse.class, reference = "C:/Desktop/hello.json")
but when swagger generate document for it then it gives following:
It is showing C:/Desktop/hello.json is not defined!
I have researched and tried lot many solutions but couldn't able to give proper reference to it. I found that this is an issue by https://github.com/swagger-api/swagger-ui/issues/1700 and https://github.com/swagger-api/swagger-js/issues/606.
So how can I use reference attribute of #ApiResponse to that swagger could show the sample XML/JSON swagger UI. My model class is below:
#XmlRootElement(name="response")
#XmlAccessorType(XmlAccessType.FIELD)
public class WebservicesErrorResponse
{
#XmlElement
private int code;
#XmlElement
private String message;
public WebservicesErrorResponse(){ }
public WebservicesErrorResponse(int code, String message)
{
this.code = code;
this.message = message;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
}
and I want to show following sample XML in the swagger UI:
<?xml version="1.0"?>
<response>
<code>200</code>
<message>success</message>
</response>
You need to annotate your model class (not the API resource/method!) with the #ApiModel and #ApiModelProperty annotations as described here.
For what you want to achieve, it would probably be enough to annotate your model members as follows:
#ApiModelProperty(example = "200")
#XmlElement
private int code;
#ApiModelProperty(example = "success")
#XmlElement
private String message;
If that doesn't work, try putting the annotation on the getters (I'm not really familiar with the XML side of this, have only done it for JSON).

Spring Cloud FeignClient decoding application/hal+json Resource<Object> return type

I'm developing a REST API using Spring Cloud, Spring Data JPA, Spring Data Rest and Spring Boot. The server implementation generates the various data items correctly, according to the HAL spec and so forth. It generates the HAL+JSON as follows:
{
"lastModifiedBy" : "unknown",
"lastModifiedOn" : "2015-06-04T12:19:45.249688",
"id" : 2,
"name" : "Item 2",
"description" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8080/pltm/accounts/2"
},
"customers" : {
"href" : "http://localhost:8080/pltm/accounts/2/customers"
},
"users" : {
"href" : "http://localhost:8080/pltm/accounts/2/users"
},
"groups" : {
"href" : "http://localhost:8080/pltm/accounts/2/groups"
}
}
}
Now I'm trying to implement the client implementation, using the FeignClient Spring Cloud library. I've defined my client interface as follows.
#FeignClient("serviceId")
#RequestMapping(value = "/api", consumes = MediaType.APPLICATION_JSON_VALUE)
public interface PltmClient
{
// Account Requests
#RequestMapping(method = RequestMethod.GET, value = "/accounts")
PagedResources<Resource<Account>> getAccounts();
#RequestMapping(method = RequestMethod.GET, value = "/accounts/{id}")
Resource<Account> getAccountAsResource(#PathVariable("id") Long id);
#RequestMapping(method = RequestMethod.GET, value = "/accounts/{id}")
Account getAccount(#PathVariable("id") Long id);
}
When I call the getAccout() method, I get back my domain object Account with the details from the JSON document. That object is a simple POJO. All of the fields are filled in properly.
public class Account
{
private Long id;
private String name;
private String description;
/** Setters/Getters left out for brevity **/
}
But when I call the getAccountAsResource(), it works and I get back a Resource object that has the data. However, the call to Resource.getContent() returns an Account object that is not completely filled out. In this case, the Account.getId() is NULL, which is causing problems.
Any ideas why this is happening? One idea I had is that the Resource class defines a getId() method, and that is somehow confusing the Jackson ObjectMapper.
The larger question is if this whole method is viable or is there a better way? Obviously, I could just use the plain POJO as my return type, but that looses the HAL information on the client side.
Has anyone successfully implemented a Java-based client implementation for Spring Data REST server endpoints?
Account not having an id is a feature of spring-data-rest. You need to enable populating of the id on the server side.
I also add a different method to return the id (in Account)
#JsonProperty("accountId")
public String getId() {
return id;
}
Enable id
#Configuration
public class MyConfig extends RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration( RepositoryRestConfiguration config) {
config.exposeIdsFor(Account.class);
}
}
I have used it here: https://github.com/spencergibb/myfeed, though I believe where I use resources, I'm using RestTemplate.