URL inside of another URL (as path) - java

I want to use the following type of URL in Restlet: http://url.com/http://www.anotherurl.com/path
As a result I want to get http://www.anotherurl.com/path as a parameter.
However it does nothing.
Also, if I use http://url.com/path , then I receive "path" without problems. http://url.com/www.anotherurl.com gives me www.anotherurl.com. However http://url.com/www.anotherurl.com/path is 404.

You need to encode the parameter special characters properly. Use URLEncoder to do so.

In fact, there are two parts here.
The URL building using the Reference class:
Reference ref = new Reference();
ref.setScheme("http");
ref.setHostDomain("localhost");
ref.setHostPort(8182);
ref.addSegment("test");
ref.addSegment("http://test");
ClientResource cr = new ClientResource(ref);
cr.get();
Getting the value as a path parameter and decode it. Here is the routing configuration in the application class:
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/test/{id}", MyServerResource.class);
return router;
}
And the corresponding code in the server resource:
public class MyServerResource extends ServerResource {
#Get
public Representation get() {
String id = getAttribute("id");
// Prints http%3A%2F%2Ftest
System.out.println("id = "+id);
// Prints http://test
System.out.println("id = "+Reference.decode(id));
return new EmptyRepresentation();
}
}
Hope it helps you,
Thierry

Related

RestTemplate GET exchange with Set<Long> parameter

I have two services (A and B). I want to send a GET request from service A to service B. Here how my request looks like (Service A):
public Set<StudentDTO> getStudentsByIds(Set<Long> ids) { //here may be a set of ids [123213L, 435564L]
return restTemplate.exchange("localhost:8090/students/all?ids={ids}",
HttpMethod.GET, new HttpEntity<>(ids), new ParameterizedTypeReference<Set<StudentDTO>>() {}, ids).getBody();
}
Here how my Service B controller looks like:
#RequestMapping("/students")
public class StudentController {
#GetMapping("/all")
public Set<StudentDTO> getStudentsByIds(#RequestParam Set<Long> ids) {
return studentService.getStudentsByIds(ids);
}
}
I am having trouble with sending set as parameter. I guess we can't put Set as parameter. I tried already turning set to String and removing the brackets from it like following and it worked:
String ids = ids.toString().substring(1, ids.toString().length() - 1);
But maybe there better solution or is there any solution to send Set?
My url looks like this: localhost:8090/students/all?ids=id1&ids=id2&ids=id3
Your url is being formed incorrectly. By using all?ids={ids} the resulting url sent to the service layer is http://localhost:8090/students/all?ids=%5B23677,%2012345,%201645543%5D. This is because the brackets from the set are being added to the url, but aren't interpreted properly. You can fix this by sending it as a comma delimited String appended to the URL, like this.
public Set<Long> getStudentsByIds(Set<Long> ids){
String studentIdsUrl = "http://localhost:8080/api/all?ids=" + ids.stream().map(Object::toString).collect(Collectors.joining(","));
return restTemplate.exchange(studentIdsUrl,
HttpMethod.GET, new HttpEntity<>(ids), new ParameterizedTypeReference<Set<Long>>() {}, ids).getBody();
}

How to send json with GET params via Spring Boot / Tomcat?

so currently I'm working on a project where we have product objects which in turn contain "Origin" objects (containing region: String and country: String).
What I'm trying to do is a RestController which takes in an optional Origin object and does something with it (e.g. logs it).
This is what I have right now:
#GetMapping("search")
public Page<Wine> getProductByStuff(
#RequestParam(required = false) Origin origin,
/* other attributes */) {
log.info(origin); // it has a proper toString method.
}
There are two problem with this approach. First of all, when I send a request like:
http://[...]/search?origin={"region":"blah","country":"UK"}
or even the html converted string like:
http://[...]/search?origin={%22region%22:%22blah%22%44%22country%22:%22UK%22}
... it says
Invalid character found in the request target [/api/products/search?origin={%22region%22:%22blah%22%44%22country%22:%22DE%22}]. The valid characters are defined in RFC 7230 and RFC 3986.
Afaik the only valid characters Tomcat has that I need are {}. All others I've replaced with the html encoded chars and it still doesn't work.
What I did to prevent this:
#Component
public class TomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
TomcatConnectorCustomizer a = null;
factory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|},\"");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|},\"");
});
}
}
(See this, which is, by the way, deprecated (at least connector.setAttribute).)
This produced:
MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type '[censored].backend.model.Origin'.
My questions are:
(How) is it possible to configure Tomcat/Spring so that they can actually accept json in the url params?
How would I format it in e.g. Postman so that it would work? Currently I'm just converting special characters by hand in the params tab of Postman.
Here is what you need to do if you want to send it as json query param.
#RestController
public class OriginController {
#GetMapping("/search")
public void getOrigin(#RequestParam(value = "origin", required = false)
Origin origin) {
System.out.println(origin);
}
}
Register a converter
#Component
public class StringToOriginConverter implements
Converter<String, Origin> {
ObjectMapper objectMapper = new ObjectMapper();
#Override
public Origin convert(String source) {
try {
return objectMapper.readValue(source, Origin.class);
} catch (JsonProcessingException e) {
//You could throw some exception here instead for custom error
return null;
}
}
}
Sending from postman
Note
My answer is not debating whether you should use POST or GET as it is not what you have asked. It is just providing one option if you want to send some payload as query param
As mentioned, don't use JSON as a path parameter.
Directly use path parameters, and convert to Origin object.
#GetMapping("search")
public Page<Wine> getProductByStuff(
#RequestParam(required = false) String region,
#RequestParam(required = false) String country, /* other attributes */) {
Origin origin = new Origin(region, country);
log.info(origin); // it has a proper toString method.
}

Using Gson for Restlet to convert Post data (Representation) to an Object

I am trying to post a form to a Restlet ServerResource and read it into an object using Gson Restlet Extension.
There's no documentation on how to use it and nothing on StackOverflow.
What is the correct way of using gson restlet extension?
Following is what I have tried so far:
public class CustomerSegment {
private int visitsMin;
private int visitsMax;
// Getters, Setters and constructors
}
public class CampaignsResource extends ServerResource {
#Post
public Representation createCampaign(Representation entity) {
Form form = new Form(entity);
// Using form is the usual way, which works fine
// form: [[visitsMin=3], [visitsMax=6]]
CustomerSegment segment = null;
// Following hasn't worked
GsonConverter converter = new GsonConverter();
try {
segment = converter.toObject(entity, CustomerSegment.class, this);
//segment = null
} catch (IOException e1) {
e1.printStackTrace();
}
GsonRepresentation<CustomerSegment> gson
= new GsonRepresentation<CustomerSegment>(entity, CustomerSegment.class);
try {
segment = gson.getObject();
//NullPointerException
} catch (IOException e) {
e.printStackTrace();
}
return new EmptyRepresentation();
}
}
Form data that is being posted:
In fact, you can leverage the built-in converter support of Restlet without explicitly use the gson converter.
In fact, when you put the GSON extension within the classpath, the converter it contains is automatically registered within the Restlet engine itself. To check that you can simply use these lines when starting your application:
List<ConverterHelper> converters
= Engine.getInstance().getRegisteredConverters();
for (ConverterHelper converterHelper : converters) {
System.out.println("- " + converterHelper);
}
/* This will print this in your case:
- org.restlet.ext.gson.GsonConverter#2085ce5a
- org.restlet.engine.converter.DefaultConverter#30ae8764
- org.restlet.engine.converter.StatusInfoHtmlConverter#123acf34
*/
Then you can rely on beans within signatures of methods in your server resources instead of class Representation, as described below:
public class MyServerResource extends ServerResource {
#Post
public SomeOutputBean handleBean(SomeInputBean input) {
(...)
SomeOutputBean bean = new SomeOutputBean();
bean.setId(10);
bean.setName("some name");
return bean;
}
}
This works in both sides:
Deserialization of the request content into a bean that is provided as parameter of the handling method in the server resource.
Serialization into the response content of the returned bean.
You don't have anything more to do here.
For the client side, you can leverage the same mechanism. It's based on the annotated interfaces. For this, you need to create an interface defining what can be called on the resource. For our previous sample, it would be something like that:
public interface MyResource {
#Post
SomeOutputBean handleBean(SomeInputBean input);
}
Then you can use it with a client resource, as described below:
String url = "http://localhost:8182/test";
ClientResource cr = new ClientResource(url);
MyResource resource = cr.wrap(MyResource.class);
SomeInputBean input = new SomeInputBean();
SomeOutputBean output = resource.handleBean(input);
So in your case, I would refactor your code as described below:
public class CampaignsResource extends ServerResource {
private String getUri() {
Reference resourceRef = getRequest().getResourceRef();
return resourceRef.toString();
}
#Post
public void createCampaign(CustomerSegment segment) {
// Handle segment
(...)
// You can return something if the client expects
// to have something returned
// For creation on POST method, returning a 204 status
// code with a Location header is enough...
getResponse().setLocationRef(getUri() + addedSegmentId);
}
}
You can leverage for example the content type application/json to send data as JSON:
{
visitsMin: 2,
visitsMax: 11
}
If you want to use Gson, you should use this content type instead of the urlencoded one since the tool targets JSON conversion:
Gson is a Java library that can be used to convert Java Objects into
their JSON representation. It can also be used to convert a JSON string
to an equivalent Java object. Gson can work with arbitrary Java objects
including pre-existing objects that you do not have source-code of.
Hope it helps you,
Thierry

POST with Restlet framework for Java

I can make a GET with no problem at all.
When trying it with a POST request, I get this message:
Internal Server Error
The server encountered an unexpected condition which prevented it from fulfilling the request
I'm testing it with Simple REST Client extension for Chrome, but I get the same message in the real application.
This is my post:
#Post
public StringRepresentation pepe(Representation entity) {
StringRepresentation result = this.users();
// Parse the given representation and retrieve data
Form form = new Form(entity);
String action = form.getFirstValue("action");
if(action.equals("add")){
//nothing
}
Db.closeConnection();
return result;
}
And this is my #Get working properly:
#Get
public StringRepresentation pepe() {
String action = getQuery().getValues("action");
StringRepresentation result = null;
result = this.users();
Db.closeConnection();
return result;
}
And the funny thing is: whenever I remove the condition if(action.equals("add")){, (which was empty inside) the POST works correctly.
This would work:
#Post
public StringRepresentation pepe(Representation entity) {
StringRepresentation result = this.users();
// Parse the given representation and retrieve data
Form form = new Form(entity);
String action = form.getFirstValue("action");
Db.closeConnection();
return result;
}
What's going on? Looks so random!
Yes, your variable action will be null if you don't have an entry action within the form payload.
You can notice that Restlet provides a method getFirstValue with a default value parameter:
String action = form.getFirstValue("action", "defaultActionValue");
This could help you not to have NullPointerException.
Otherwise, it seems that you try to implement several actions for a method POST. I think that this blog post could give you some additional hints: https://templth.wordpress.com/2015/03/20/handling-multiple-actions-for-a-post-method/.
Hope it helps you,
Thierry

Restlet, An example of a get with parameters

Just startred using restlet with java and was pleasently surprised how easy it was. However this was with puts. I then started to work with get but couldn't work out how to pass infromation with the get.
With the put it was easy as:
#Put
public Boolean store(Contact contact);
But when i try and do this with get it doesnt work. From reading around i think i have to not pass it any parameters and just have this:
#Get
public Contact retrieve();
and then pass the parameters in a url or something?
But i cant find any info on how to do this. As with put i could just use:
resource.store(user1);
Any help please?
Im pretty sure this is the kind of thing i just need to see an example of and then ill be able to do it easily. Example of how to get the infromation out of the url at the other side would be very helpful aswell.
Thanks
I now have on my client side:
String username = "tom";
ClientResource cr2 = new ClientResource("http://.../ContactManager/contacts/" + username);
ContactResource resource2 = cr2.wrap(ContactResource.class);
resource2.logIn();
On the server side i have:
#Get
public Contact logIn(){
System.out.println("name is " + resource.getAttributes().get("contactId"));
return null;
}
But i am not sure what resource is? It doesnt exist in my program and am not sure what type it needs to be or where to declare it.
A good approach with REST is to specify this contact id within the URI. Something like that: /contacts/mycontactid.
When attaching your resources within the application class, you can define this segment as an attribute (the contact id one in your case).
public class ContactsApplication extends Application {
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/contacts/", ContactsServerResource.class);
router.attach("/contacts/{contactId}", ContactServerResource.class);
return router;
}
}
Then you can have the code provided by Richard in his answer.
Hope it helps you.
Thierry
I know this question was asked along time ago but the answer I think you are looking for is:
Application Code
public class ContactsApplication extends Application {
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/user/", ContactsServerResource.class);
router.attach("/user/{user}", ContactServerResource.class);
return router;
}
}
Resource Code
#Get
public void login()
String userName = (String)this.getRequestAttributes().get("user");
The (String)this.getRequestAttributes().get("user"); allows you extract details from the URL.
Hope this helps
It seems that what you are looking for is something like:
public final Representation get() {
String contactId = request.getAttributes().get("contactId"));
// Find the Contact object with that id
JacksonRepresentation<Contact> result =
new JacksonRepresentation<Contact>(contact);
return result;
}
Also see: how to pass parameters to RESTlet webservice from android? for a similar approach.

Categories