I want to develop rest api.such as:
http://localhost:8080/TestSomeWay/resources/test/create?meg=sadasd&name=sadasd
and get params from urlparams exp."meg"&"name"
I am using jersey to develop a Restful post method
it dose not make it out
code:
#POST
#Path("/create")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.TEXT_PLAIN)
#Override
public String create( #FormParam("meg")String megString, #FormParam("name")String nameString) {
TestUser testUser=new TestUser();
testUser.setMeg(megString);
testUser.setName(nameString);
em.persist(testUser);
em.flush();
return testUser.getId().toString();
}
You seem to be confused as to what you are trying to achieve, and that's showing up as an incoherent API. Once you've gone wrong that way, it's small wonder that things are going wrong!
First off, you've got to figure out whether you're using GET, PUT or POST, and in the latter two cases, what is the content type (or types) that you are consuming, as both PUT and POST are typically dealing with a document incoming. Moreover, if you're doing anything that isn't idempotent (i.e., so that it would be “the same” if you did it twice in a row as if once) then you should definitely be using POST; the classic example is paying for some goods, which you definitely don't want to do twice, whereas setting your preferences can be idempotent. The final complication is that it is usually bad style to mix query parameters with a body; either the parameters are in the query part or they are in the body (or they are in the path, but in that case you're dealing with different resources conceptually).
If you're just dealing with HTML forms, the two styles of method you'll want will be like this:
#GET
#Path("/create")
#Produces(MediaType.TEXT_PLAIN)
public String createFromGet(
#QueryParam("meg") String meg,
#QueryParam("name") String name) {
...
return theString;
}
#POST
#Path("/create")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.TEXT_PLAIN)
public Response createFromPost(
#FormParam("meg") String meg,
#FormParam("name") String name) {
...
return Response.created(theNewUrl).entity(theString).build();
}
The first deals with a GET on a URL like /create?meg=foo&name=bar and the second deals with a POST to a URL like /create. However, given the name “create” I'd be tempted to go with just using the POST version and to not try to support encoding the parameters in the query part; creation is one of those things that tends to not be idempotent.
Note that I have assumed that your creation is making a resource (that's good RESTful programming!) so I adjusted to return the right kind of response; it's a bit more involved than the usual, but is exactly the right thing.
You should QueryParam instead of FormParam to obtain the functionality you want
Related
I'm new to java / rest / jersey, so please bear with me.
Problem: I want to use a GET method to retrieve some information I have previously POSTed. My only confusion is...where is the information after I call the GET?
Here is my post (you can ignore it as I know this part works):
#POST #Path("/tools")
public Response createTool(JsonNode tool) throws URISyntaxException {
//use custom validator to de-couple implementation from validation
ToolValidator validator = new ToolValidator(tool);
if(validator.isNotNull() == false) {
System.out.println("in not null");
return Response.status(400).entity("Please add tool details").build();
}
if (validator.hasValidType() == false) {
System.out.println("in valid timestamp");
return Response.status(400).entity("Please provide a valid tool type").build();
}
if(validator.haveValidValues() == false ) {
System.out.println("in valid values");
return Response.status(400).entity("Please provide a tools list with valid numerical values").build();
}
String type= tool.get("type").asText();
return Response.status(201).entity(tool).build().created(new URI("/tools/"+type)).build();
}
It definitely posts a tool correctly (I've tested it and seen it, I believe this works but it might not).
Now what I want to do is get the json, see if adheres to some requirements, and return an answer. Basically, irrelevant of the POST code above, for any url that returns some json when you navigate to it, I want to be able to grab that json and do something with it.
#GET #Path("/tools/{type}")
public Response getToolInfo(#PathParam("type") String type) {
return Response.ok().build();
}
My only question here is..."where" is the information to manipulate?? I feel like I'm asking an extremely fundamental, basic question for REST / Jersey, so I apologize if this is like the ABCs here.
Basically, I use my POST method to POST a tool with some specific json. I can navigate to that page and see the json object. Now I want to use a GET method to analyze the json but...I have no idea how / where this information is stored when I decide to use a GET method.
Does the #get annotation automatically store the information returned from the specified path somewhere? Does anything?
From the code you pasted it doesn't look like anything is stored. In your #POST it looks like you pass JsonNode tool, from that you create an object of type ToolValidator which you use to do some sort of validation. From your JsonNode tool you retrieve a field called "type".
At last you create a response instance with the parameter you already passed and you change the Uri to "/tools/" and the String value of whatever is in the variable "type".
So it doesn't look like you are doing any database access to store the JsonNode passed as parameter.
It definitely posts a tool correctly (I've tested it and seen it).
It posts the tool because of this Response.status(201).entity(tool).build()
You are just simply telling it to set the response entity in the builder. Whereas build() a response instance.
For testing purposes only create a global variable, in your class, of type JsonNode and copy the passed parameter (JsonNode tool), in your POST method, to your global variable. Then you can retrieve it in your GET method. Don't forget to create getter and setter for your global variable.
In a real life scenario though you would be storing things in your database. If it's just for some prototype then a global variable should be enough
Here is a tiny example of what I mean:
public class SomeClass {
private JsonNode copiedTool;
#POST #Path("/tools")
public Response createTool(JsonNode tool) throws URISyntaxException {
setCopiedTool(tool);
String type= tool.get("type").asText();
return Response.status(201).entity(getCopiedTool()).build().created(new URI("/tools/"+type)).build();
}
#GET #Path("/tools/{type}")
public Response getToolInfo(#PathParam("type") String type) {
if(getCopiedTool().get("type").equals(type)) {
return Response.ok(getCopiedTool()).build();
}
return Response.status(204).build();
}
public JsonNode getCopiedTool() {
return copiedTool;
}
public void setCopiedTool(JsonNode copiedTool) {
this.copiedTool = copiedTool;
}
}
I have to following endpoint structure in Jersey:
/objects/
/objects/:id
/objects/:id/:field
/objects/:id/:field/:subfield
The IDs I'm using have a very specific format, so I first check if the format is valid before making a request to the database.
Right now I have to put this code in each of the POST, PUT, GET, DELETE functions for each of the functions that has :id as a parameter. So this just means an early return.
if (!isIdValid(id)){
return Response.status(Response.StatusType.BAD_REQUEST)
.entity("The ID you've provided is invalid")
.build();
}
(In reality the error entity is an object containing more information about the error)
And then for each function using the :field or :subfield parameters the code is similar. This checking and error-handling behavior has to be copied every time. And when I start copy-pasting stuff, I start thinking: there should be a better way?
I would like to place the :id checking code at the the /objects/:id level, and then all further nested levels are assumed have a valid ID. The same for the other parameters further nesting down.
I've been looking into using subresource locators, but then you create a function returning a new instance of the subresource. I can't put a conditional return of a Response-object at that level for if the validation fails.
#Path("{id}")
function Class<ObjectFieldResource> getObjectById(#PathParam("id") String id){
return ObjectFieldResource.class;
}
I could start throwing exceptions, but I would rather avoid that, since I don't really consider invalid input to be an exception.
How would such a structure best be implemented? I've looked at bean validation but that doesn't seem to allow me to define validation for my specific format + custom error responses.
Am I missing something in the way subresources should be implemented?
Solution 1
If you can use regexp checks instead of your isIdValid method it's possible to define your resources like this
#POST
#Path("objects/{id:\\d+}")
public Response doSmth(#PathParam("id") String id) {
...
}
In a case of invalid id format caller will have 'Not Found' response status without even reaching your doSmth method.
Obviously, you can use String constants for all equal path values.
final static String ID_RES = "objects/{id:\\d+}";
#POST
#Path(ID_RES)
public Response postSmth(#PathParam("id") String id) {
...
}
...
#GET
#Path(ID_RES)
public Object getSmth(#PathParam("id") String id) {
...
}
The can also read full description of Path#value parameter
Solution 2
Create and register at your REST server javax.ws.rs.container.ContainerRequestFilter implementation with filter method having needed URI checks.
The single filter parameter has ContainerRequestContext type from witch you can call getUriInfo for getting URI and method abortWith(Response response) which can be used for aborting caller request if your resource ids validation was failed.
See Chapter 10. Filters and Interceptors chapter of Jersey Manual.
Say I have a Jersey Resource somewhat similar to this:
#Path("/test")
public class TestResource{
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response test(List<JSONRepresentation> json){
//some logic that gives different responses, all just Strings
}
}
And a RestyGWT Service that consumes it like this:
#Path("api/test")
public interface TestService extends RestService{
#POST
public void test(List<JSONRepresentation> json, MethodCallback<String> callback);
}
The thing is that, when I try to access the Resource using the Service, if the List isn't null or empty I get an Internal Server Error that doesn't have anything to do with the server code because I can't even debug the test method logic.
However, when the List is null or empty, the Resource's logic does what it should and I can debug it without any problems.
The contents of the JSONRepresentation don't really seem to matter for this problem.
I have no idea why this is even happening and I couldn't really find any similar questions around, any help would be really appreciated.
If you want to send Strings back to the client, change your code to:
#Path("/test")
public class TestResource{
#POST
#Produces(MediaType.APPLICATION_JSON)
public String test(List<JSONRepresentation> json){
//some logic that gives different responses, all just Strings
}
}
and change the code of your resource to:
#Path("api/test")
public interface TestService extends RestService{
#POST
public void test(List<JSONRepresentation> json, TextCallback callback);
}
}
hope that helps.
Ok, I somehow figured out why the error happened and how to avoid it.
The thing is, the problem was actually in my json representation, I had defined it with private variables and getters and setters for each one of them (I have some sets and instances of other json representations on it along with other String and primitive variables)
The thing is that, for some reason I'd really want to know more about of, if a variable with a type of another json representation is set as private, this error happens
So, I just set that variable as public and everything worked fine, the odd part is that Collections of another json representation classes work fine even as private variables (primitives, String, and Date work fine like that too).
I've developed REST services, but now I realized that I'm doing something wrong.
For example, I have a service which retrieves information about a specific device. Each device has an address: sector.room.group.id.
The URI I did for this GET method was: (...)/services_devices/{sector}/{room}/{group}/{id} But now I realized that I should not have used the '/' to separate the device address, right?
How should I pass the address to this method? Using ';' ?
My GET method is:
#GET
#Path("{sector}/{room}/{group}/{id}")
#Produces("application/json")
public String getDeviceName(#PathParam("sector") int sector, #PathParam("room") int room, #PathParam("group") int group, #PathParam("id") int id) throws Exception
{
String name = null;
try {
name = new DevicesManager().getDeviceName(sector, room, group, id);
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
There is a simple way of change this, to have a correct URI? I have this "error" in many methods.
If there is a hierarchy in your resources path variables are appropriate.
It seems in your case there is a hierarchy between devices and address, but first comes the address and after the deviceName. "deviceName" can be considered a one more hierarchy step.
The best way to reflect the above relations would be the following url:
(...)/sector/room/group/id/deviceName
You can then have another attribute of the device mapped like this:
(...)/sector/room/group/id/deviceOwner
The JAX-RS mapping would be:
#GET
#Path("{sector}/{room}/{group}/{id}/deviceName")
#Produces("application/json")
public String getDeviceName(#PathParam ...) {
//impl.
}
And yes, if the deviceName is the only relevant attribute of the resource, then you can leave out "deviceName" and your orignal mapping is correct.
If the resource at /sector/room/group/id has many attributes you should consider returning a composed object for the path:
#GET
#Path("{sector}/{room}/{group}/{id}")
#Produces("application/json")
public Device getDeviceName(#PathParam...) {
}
REST architectural style introduces HATEOAS, which means that client and server are loosely coupled. Simply the client is not aware of how the URLs look like and gets them from previous responses. (it's similar like surfing thru HTML pages). Of course there will be at least one URL, an entry point, that is known to the client. From this point of view, your need to have correct URIs is irrelevant. What's correct URI? The URI is correct when its form is aligned with RFC.
You are probably introducing URL patterns, that are not RESTful, because it implicates tight coupling between client and server (the client must be aware of URL patterns and have ability to construct URLs from them; fill up sector/room/ etc. in your case)
See also this post:
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
My advice is; don't waste your time on URL patterns, make URLs simple as is possible, flat hierarchy has also many benefits, and follow HATEOAS principle.
My service:
#POST
public String setData(#QueryParam("id") Long is, MyObject payload) {
...
}
or
#POST
public String setData(#PathParam("id") Long is, MyObject payload) {
...
}
My interceptor on the server:
Object read(MessageBodyReaderContext context) throws IOException, WebApplicationException {
Class mypayloadtype = context.getType;
InputStream mypayloadinpustream = context.getInputStream();
Long myidparam = ???????? // how to get the query or path param here?
}
EDIT: To be a bit more concrete:
What I'd like to do is to grab the XML and store it based on the parameters in a separate audit system. Maybe PreProcessInterceptor / PostProcessInterceptor are the better choices?
Any hints or alternative ways to get the param when the xml is still available for preprocessing?
Miguel
I just stumbled over the same problem today. I needed the #PathParams and #QueryParams in the read() method and ended up with something like this:
public class MyInterceptor implements PreProcessInterceptor, MessageBodyReaderInterceptor
{
private static ThreadLocal<UriInfo> uri = new ThreadLocal<UriInfo>();
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
{
uri.set(request.getUri);
...
}
public Object read(MessageBodyReaderContext context)
{
String param = uri.get().getPathParameters().getFirst("myidparam");
...
}
}
Although when thinking about it now - I'm not quite sure, if just using PreProcessInterceptor/PostProcessInterceptor will also do the trick for my (and maybe your) problem. I'll have another look tomorrow.
I am not an expert on the topic but to me it seems as if the MessageBodyReaderContext interface does not really know if it is on the server or the client side, so it cannot expose the request or its parameters / path parts etc.
So as far as I know this is not possible.
If your code knows that it lives on the server side of the rest
communication, maybe you can use a servlet filter to store the request
in a ThreadLocal and then access it from there while the request is
handled, somewhat similar to RequestContextFilter / RequestContextHolder from the spring framework? (Then the request object does not know anything about the annotations of your service, but instead one has to extract the information manually from the request. This means to have the same information in two places, so there has to be a better solution ...)
Edit: after looking at some examples I get the vague feeling that if you want to read the input stream to create an object and add path parameters to it, MessageBodyReaderInterceptor is simply not the way to go. Instead set up a MessageBodyReader which constructs the object from the request body data, and this then will be passed into the public String setData(#PathParam("id") Long is, MyObject payload), assuming that this method is annotated with a #Consumes which matches the #ConsumeMime annotation for the MessageBodyReader. There you might be able in the setData to set the missing id on the object read from the request body. Some examples related to this seem to be here: How to get full REST request body using Jersey? (but for Jersey, not jBoss :-/)
However I am not sure if that works for you, and I also feel I completely overestimated my ability to answer this question appropriately, so I hope someone more knowledgeable comes in with a better solution.