I use a RESTful Web Service. In this web service I must pass a bean that I want to save as a parameter.
Here is the server code:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Unidade inserir(Unidade unidade){
Session s = ConnectDb.getSession();
try {
s.getTransaction().begin();
s.save(unidade);
s.getTransaction().commit();
return unidade;
} catch (Exception e) {
e.printStackTrace();
s.getTransaction().rollback();
return null;
} finally {
s.close();
}
}
I have the following code in the client:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Builder builder = webResource.accept(MediaType.APPLICATION_JSON);
GenericType<Unidade> genericType = new GenericType<Unidade>() {};
Unidade u = new Unidade();
u.setUnidSigla("KG");
//How to pass this bean as parameter?
Unidade response = builder.post(genericType);
System.out.println(response);
How can I pass the bean to the method as a parameter?
Using Jackson as a Serializer/DeSerializer
If your Unidade object is annotated with Jackson and/or a Deserializer is registered then you should be able to POST with a BODY that contains the JSON representing the Unidade object. It should be magically deserialized and rebuilt as an object on the server side.
Important
Make sure that you add a Content-Type header in the POST request with a value of application/json. Without this header your Jersey might not know what to do with the body.
You would use the Jackson ObjectMapper to serialize your Unidade object to JSON and send that instead of whatever that GenericType stuff is.
I have both Jersey and RESTEasy implementations that work seamlessly with Jackson in this manner.
How can I pass the bean to the method as a parameter?
Checkout the documentation for the post method:
/**
* Invoke the POST method with a request entity that returns a response.
*
* #param <T> the type of the response.
* #param c the type of the returned response.
* #param requestEntity the request entity.
* #return an instance of type <code>c</code>.
* #throws UniformInterfaceException if the status of the HTTP response is
* greater than or equal to 300 and <code>c</code> is not the type
* {#link ClientResponse}.
* #throws ClientHandlerException if the client handler fails to process
* the request or response.
*/
<T> T post(Class<T> c, Object requestEntity)
throws UniformInterfaceException, ClientHandlerException;
The method takes two parameters. First parameter is the expected response type, and second one is the entity which is going to be put in the request body.
What happens here, when performing the request Jersey would serialize the object passed as a request entity into the JSON string (hence you've set the header - accept(MediaType.APPLICATION_JSON)). When the response from the server arrives, Jersey will deserialize it (the inverted process as in case of requestEntity) and return you the object.
And what if my method receives more than 1 parameter? Because the post
method only acepts 1
Well you cannot do it with JAX-RS, and it makes little sense actually. You can pass multiple parameters to the method as #PathParam or a #MatrixParam, but there can be only one associated with the body (well you have only one body in our request, right?). Checkout answer to this question and checkout how to use #PathParam or #MatrixParam
Let's suppose instead of returning a "Unidade" class, my method
returns a String. So, it will receive a "Unidade" as parameter and
return a "String". How can i retrieve it in this case, passing the
"Unidade" instance as before?
I believe you could achieve that with post(String.class, unidadeInstance). The first parameter doesn't have to be the same as the second. It's valid to accept one parameter and return another. It is even valid to take a parameter and return nothing in the body (like you have done it in the code attached to your question). You could accept the body and send back response containing status 201 Created and Location header entry pointing to the URL of the newly created resource.
Not sure what's your purpose for GenericType. Anyway, try the code below.
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
Unidade u = new Unidade();
u.setUnidSigla("KG");
WebResource webResource = client.resource("http://localhost:8080/RestauranteWeb/rest/unidades/7");
Unidade response = webResource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.post(Unidade.class, u);
I'm not sure if it helps but i had a similar problem.
My scenario was i need a webservice which had to receiver a bunch of values which are organized as a kind of profile. But this service has to handle that there are comming more profiles where still old clients using the service. The interface had to be as static as possible.
Our solution is very simple. We only post a single text field as content of the post. But this includes the serialized state of an profile object in JSON.
Pseudo code:
public class Profile1 {
...
public String asJSON() {
JSONObject obj = new JSONObject();
obj.put("profileAtr1", profileAtr1);
...
return obj.toString()
}
}
formParams.put("profile", profile.asJSON());
client.post(formParams);
This way it's not automaticly deserialized but it's easy to do it by hand.
We do this with a generic Profile object which can be created out of JSON String in the constructor.
Pseudo code:
public GenericProfile {
public GenericProfile(String json) {
JSONObject obj = new JSONObject(json);
String profileName = obj.getString("profileName");
if (profileName.equals("Profile1") {
this = new Profile1(obj); // I know this is not working ;) My impl. is a litle bit more complecated as that. I think i use a static method in the generic profile to create an instance i need.
} ...
}
}
And then in your webservice only have this one form param to handle and to deserialize ;)
Pseudo code:
public ResponseEnvelope coolServiceFunction(#FormParam("profile") String profileData) {
GenericProfile profile = new GenericProfile(profileData);
if (profile instanceof Profile1) {
do what you want
}
}
Sorry for the pseudo code, but i had alleady shutdown my dev vm and have no access to any repository any more :(
I think the biggest benefits from this solution is:
1. It can transport anything you can pack in JSON. I transfer BASE64 encoded binary blocks and heavyly encrypted textdata this way.
2. The easiest tutorial example of your REST Framework of the POST Service will provide all you need to do this.
3. You can be sure that your interface will stay for a long period of time.
Hope that helps
Related
I am trying to pass the List of String from one server to the another server in spring boot.
How i can get that list at the another server?
The code i have tried-
public void addNewMostPopular(List<String> totalList){
try {
HttpHeaders httpHeaders = getHttpHeaders();
HttpEntity<String> httpEntity = new HttpEntity<String>(null, httpHeaders);
ResponseEntity responseEntity = restTemplate.exchange(
BASE_URL + "addMostPopular/"+new ArrayList<>(totalList), HttpMethod.POST, httpEntity,TrendingCategoryDTO.class);
}
and at server side i tried to get like-
#RequestMapping(value="/addMostPopular/[{totalList}]", method = RequestMethod.POST)
public void addMostPopularProduct( #PathVariable List<String> totalList) {}
Past long object in the url is a bad praxis, thats because spring url interpreter has a maximun lenght, so if you pass more than 2048 or 4096 char in some cases your request will return Response 400 bad request and won't execute anycode on your spring server.
After this aclaration, is there any option to pass a list? Yes, of course! But we need use #RequestBodylike this:
#PostMapping("/addMostPopular")
public void addMostPopularProduct(#RequestBody List<String> totalList) {
// Your function
}
Now we need to add to our other server the List we want to pass to this request in the body of the request.
If you like to pass a List of values in the url one possibility is to pass them as url parameters.
You have to create a link similar to the followings:
http://youserver/youraction?param=first¶m=second¶m=third
or
http://youserver/youraction?param=first,second,third
Your controller in spring must be something like
#Controller
public class MyController {
#GetMapping("/youraction")
public String yourAction(#RequestParam("param") List<String> params) {
// Here params is tre list with the values first, second, third
}
}
This action is able to handle both kind of requests that I wrote before.
There are many ways to pass infomation between servers.
The simple way is to initiate an http request, based on your request method get or post put the parameters to the appropriate location : reuqest header or request body. You can do like #Davide Lorenzo MARINO.
Or use a message queue, like ActiveMq.
In the case of the same registry center, you can also use #feign to resolve it.
I'm working on Android app which connects to REST and invokes a method. This is Embarcadero REST DataSnap.
Using parameters like "#Query" is good when you invoke method like that:
www.app.net/api/searchtypes/862189/filters?Type=6&SearchText=School
However, here methods are invoked differently:
/datasnap/rest/some_class/some_method/some_parameter
Below is simple class to handle parameter which goes in request body:
public class Dane {
private int NAGL;
public Dane(int NAGL) {
this.NAGL = NAGL;
}
}
When I try to use Retrofit annotation #Query, for example:
#POST("datasnap/rest/TstBaseMethods/%22SelectSQL%22/{aSelectSQL}")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials,#Body Dane json,#Query("aSelectSQL") String aSelectSQL);
String dane = Credentials.basic("admin","admin");
Dane json = new Dane(11101);
Call<Logowanie> sql = gerritAPI.selectSQL(dane,json,"select n.datadok from nagl n where n.id_nagl =:NAGL");
and I launch the app, I see in logs
TstBaseMethods.SelectSQL: {aSelectSQL} << {"NAGL":11101}
The content of aSelectSQL is not sent to the server. I've already noticed that if I hardcode the content into URL and I invoke this as below, it works:
#POST("datasnap/rest/TstBaseMethods/%22SelectSQL%22/select%20n.datadok%20from%20nagl%20n%20where%20n.id_nagl%3D%3Anagl")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials,#Body Dane json);
Is there any way to pass properly content of the parameter to the server? It won't be good to hardcode all parameters in URL.
So, in retrofit, the #Query annotation is used for query parameter.
It will add your parameter as a query parameter, for example:
#GET("/api/somePath")
Call<JSONObject> getSomething(#Query("foo") String foo);
...
service.getSomething("bar")
Will actually result in the url:
https://yoursite.com/api/somePath?foo=bar
Here, in your case, you are using {} inside the url, which indicates retrofit that you are adding a path parameter. So your post should be like this
#POST("datasnap/rest/TstBaseMethods/%22SelectSQL%22/{aSelectSQL}")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials,#Body Dane json,#Path("aSelectSQL") String aSelectSQL);
I am new to java and creating some Restful services in netbeans using jersey framework.
I have created many GET, POST web services which is having different different type of responses which are basically Model Objects and depending upon media type I am getting JSON or XML.
Some response is having only a single object which is being shown in JSON inside {}, and some are list which are in [].
I want to see a generic response format for all api responses.
Example-
{"status":"0 or 1", "message":"any string message","result":"it can be a single object or a list of objects which is dynamic depending upon each web service response"}.
Since here in java we need to created model objects for getting responses so I have created a ResponseModel which is having a status property , message property but don't know the type of result property because sometime it can have a single object or sometime a list so what type should I set for this property so that I can assign any thing to this and response will always be in same format for JSON or XML.
I have created a static method with constructor which takes all these three parameters and create a ResponseModel object.
Thanks in advance
EDITED- Code after Using "Object" as generic type
public static Response getResponseObjectIsValid(boolean isValid, String message,Object result)
{
if (isValid == true) {
LGSResponse response = new LGSResponse();
response.setStatus(1);
response.setMessage(message.length()>0 ? message : "Success");
response.setResult(result);
return Response.status(Status.OK).entity(response).build();
}
else
{
LGSResponse response = new LGSResponse();
response.setStatus(1);
response.setMessage(message.length()>0 ? message : "Failed");
response.setResult(result);
return Response.status(Status.OK).entity(response).build();
}
}
Result Parameter is a normal model object.
You can simply create your class as you've said.
public class ResponseModel {
int status;
String message;
Object result;
// contructor, getters, setters go here...
// (probably consider using lombok, but that is a story for another time)
}
Then you can pass either a single object or an array as the 3rd param and the json/xml serializer should take care of the conversion for you unless your objects are very complex(I rarely run into that problem)
For example, your Jersey methods would look something like this:
// returning a single object within the response model
#GET
#Path("/myMethod")
public Response aJerseyMethod() {
Book aBookObject = new Book("The Title", "Name of Author");
ResponseModel responseModel = new ResponseModel(1, "This is a book", aBookObject);
return Response.ok(responseModel)
.build();
}
// returning an array within the response model
#GET
#Path("/myOtherMethod")
public Response anotherJerseyMethod() {
Book aBookObject = new Book("The Title", "Name of Author");
Book anotherBookObject = new Book("The Other Title", "Name of another Author");
ArrayList<Book> aBookArray = new Arraylist();
aBookArray.add(aBookObject);
aBookArray.add(anotherBookObject);
ResponseModel responseModel = new ResponseModel(1, "These are books", aBookArray);
return Response.ok(responseModel)
.build();
}
In both cases you should get the expected output you were talking about and you do not have to do any additional checks or conversions yourself.
I just wrote this here, so try it out with your own classes instead of "Book" (which is not a real class) and let me know if it works.
You should not maintains status code, message and result within json body.
Response message should only contain the result of that api.
Httpstatus code will be the status code (Ex.200 is success, 404 is notfound)
All the other info should be maintained in response headers, not within json body
I want to create a REST API client, which for the same REST API server will call different URLs. This different calls will return JSON and XML to the client.
What would be a good design pattern for this situation ?
So far I've come up with a combination of Strategy and Command:
public interface IRestCall {
/** Setup the URL for the call. */
void setup(IRestSetup setup) throws Exception;
/** Execute the call, using the URL set up in the first step.
* #throws Exception */
void call() throws Exception;
/** Process the result and return to the user. */
<T> T getResult(IRestResultProcessor<T> result) throws Exception;
}
This is the Strategy interface. The context for the Strategy will
be in some Get/Post/Put methods in a Facade class.
IRestSetup and IRestResultProcessor are interfaces for Command objects
which will setup the URL for the REST API and will process the result.
I don't think you need some special design pattern to handle that situation. I personally would just add some generic methods for GET and POST requests. The code below handles only GET requests but it should be enough to get an idea what I mean.
Your client method processing GET requests might look something like
/**
restUrl - url to the service
resClz - the actual POJO object that the JSON or XML response will be mapped to
resType - type of MIME object returned by the server (typically MediaType.APPLICATION_JSON or MediaType.APPLICATION_XML)
*/
<T> T doRestGet(String restUrl, Class<T> resClz, String resType){
T obj;
Client client = ClientBuilder.newClient();
WebTarget target = client.target(UriBuilder.fromUri("http://localhost:8088/Your_WS_root").build());
try{
obj =(T) target.path(restUrl).request()
.accept(resType).get().readEntity(resClz);
}catch(Exception e){
System.out.println(e);//log or propagate the exception
return null;
}
return obj;
}
Your server method might look something like
#GET
#Path("/userMsg")
#Produces(MediaType.APPLICATION_JSON)
public UserMessage getUsersOne() {
UserMessage uMsg = new UserMessage();
uMsg.setUserMsg("successful call");
return uMsg;
}
Now you'd call your service
UserMessage uMsg = (UserMessage)doRestGet("userMsg", UserMessage.class, MediaType.APPLICATION_JSON );
This is my method for creating Response with header parameters and body:
public Response sendOKResponse(request req)
{
ResponseBuilderImpl builder = new ResponseBuilderImpl();
// set the header params.
for(int index =0; index<req.headerParameters.size(); index++)
{
builder.header(req.headerParameters.get(index).getName(), req.headerParameters.get(index).getBody());
}
// set the body and response code
builder.status(Response.Status.OK).entity(req.getBody());
Response r = builder.build();
return r;
}
And this is how i return the Response:
Response response;
response = sendBadMesseage();
return response;
This code returns code 204(No content) instead of 200.
Any ideas why?
You shouldn't be instantiating your response builder with new, the whole point of the JAX-RS abstraction layer is to hide implementation details away from calling clients. This is what makes it possible to have various vendor implementations which can be interchanged at will. Also, if you are using JEE6, or hope to migrate to it, this code will almost certainly fail. Most JEE6 vendor implementations utilize CDI, which is concept-incompatible with usage of new. But, closer to the topic, the JAX-RS implementation specifies that a 204 status code be returned if a responses wrapped entity is null. You might want to verify this is not the case in any of your methods. Also, you might want to make some changes to your code:
public Response sendOKResponse(request req) {
ResponseBuilder response = Response.ok();
// set the header params.
for(Header h: req.headerParameters()) {
builder = builder.header(h.getName(), h.getValue());
}
// set the body and response code
builder = builder.entity(req.getBody());
return builder.build();
}
Your sendBadMessage method should also look similar to above. You can log your entity before adding it to the builder, to verify that you only get a 204 when it's null.