I have a REST endpoint /v1/abc.
It is a POST method.
In the request body i send an object Test which looks as below.
Class Test {
Boolean flag = null;
String name = null;
}
I invoke the endpoint using swagger.
In the request body, i set below.
{
"name" : "hello"
}
If you notice, I am not sending flag at all.
In my API, i want to test if the request object has flag or not. If not present, i throw an exception.
Can you tell me how to achieve this?
I assume that you have a simple POST method like this one:
#PostMapping(path = "/v1/abc")
public void addAbc(#RequestBody Test test) {
//code
}
So you could just check test.getFlag() != null and thereafter raise an exception or not.
As an alternative you could add #Valid to your method parameter to enable JSR 303 bean validation and make use of #NotNull on your flag field.
Little remark: the keyword class in your example has to be written in lowercase.
Related
I have developed a REST end-point in Springboot that takes a String ID and responds with a ModelAndView. This end-point is marked with #Cacheable annotation. Now there are two things that can happen at the given end-point.
Case 1: The request ID exists in the DB and yields a URL to which redirection needs to happen. In this case, response should be cached so that upon consecutive requests of the same ID, the result can be served from Cache
Case 2: The requested ID doesn't exist in the DB and thus redirection should happen to a specific URL and no caching should be done in this scenario.
So below is my method
#GetMapping("{id}")
#Cacheable(value = "url-single", key = "#id", unless = "#result.view!=\"redirect:/notfound\"")
public ModelAndView redirect(#PathVariable("id") String id, ServletRequest servletRequest,
ServletResponse servletResponse) {
HttpServletRequest request = HttpServletRequest.class.cast(servletRequest);
LOG.info("Redirection request from: {} for Short URL Key: {}", request.getRemoteAddr(), id);
try {
Optional<String> originalUrlOptional = urlManagerService.retrieveOriginalUrl(id);
if (originalUrlOptional.isPresent() && !StringUtils.isEmpty(originalUrlOptional.get())) {
LOG.info("Found Original URL: {} for Short URL Key: {}", originalUrlOptional.get(), id);
return new ModelAndView("redirect:https://" + originalUrlOptional.get());
}
} catch (NoSuchElementException e) {
LOG.error("Error while redirecting: {}", e.getMessage(), e);
}
return new ModelAndView("redirect:/notfound");
}
If I understand it correctly from here, the keyword unless in #Cacheable applies to the return type and in order to access any particular member variable of the return type object, we have to refer to it as #result.attributeName <comparison> <value>.
So why isn't anything being stored in my Redis cache? If I remove the unless condition, everything gets stored. Is the condition not correct?
I have been looking at your unless statement:
unless = "#result.view!=\"redirect:/notfound\""
so this means that it WONT be cached if the result.view IS NOT redirect:/notfound.
My assumption is that you want to cache it, except when redirect:/notfound.
There is a "double negative" here.
So you probably should use:
unless = "#result.view==\"redirect:/notfound\""
(so use '==' instead of '!=')
Let me know if this works!
I found where I was going wrong after playing with the unless conditional keyword for awhile. The #result inside the double quotes (") of the unless keyword is literally the object to returned. What that means is that, you can call whatever method can be called on that object.
The statement "#result.view==\"redirect:/notfound\"" was failing because the object didn't expose any member variable called view. Using the expression unless = "#result.getViewName().equals(\"redirect:/notfound\")" actually did the trick because there was a method getViewName() on the object that returned String and calling equals() on it did the actual comparison.
I guess I got stuck here because I wasn't not familiar with Spring Expression Language or SpEL
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 want to know if it's possible to map the same URL to different methods in the RestController class, based only in the request body. For example:
#RequestMapping(value="/delete", method=RequestMethod.POST )
public void delete(#RequestBody String id) {
//do something
}
#RequestMapping(value="/delete", method=RequestMethod.POST )
public void delete(#RequestBody Book book) {
//do something
}
The request body will always be a JSON payload. if it's {"id":"foo"} I want the first method to be called. If the request body is:
{
"title":"Spring Guide",
"author":"John Doe"
}
I want the second method to be called. Is this possible?
There is no way to differentiate only by payload.
Based on the tests I did here and M. Deinum and Ali Dehghani's response I think the best way to do this is to have different urls for each case. So a /books/{id} to delete by the id, and a /books with the object's JSON in the body to delete passing the object. Thanks for all that commented.
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.
I have a Jersey based Java servlet:
#Path("foo")
class Foo {
#GET
#Path("bar")
public Response bar(#QueryParam("key") String value) {
// ...
}
}
I can call it in Tomcat just fine as:
http://localhost:8080/container/foo/bar?key=blah
However, in my JerseyTest, using Grizzly, it's not handling the parameters properly. This test case returns a 404 error:
#Test
public void testBar() {
final Response response = target("foo/bar?key=blah").request().get();
}
I suspect the issue is it's looking for a resource named foo/bar?key=blah rather than trying to pass key=blah to the resource at foo/bar. If I pass just "foo/bar" to target(), I get a 500, as the code throws an exception for a null parameter.
I looked through the Jersey Test documentation, and some examples, and I found some cryptic looking stuff that might have been for passing parameters to a GET, but none of it looked like it was assigning values to parameters, so I wasn't positive how I would use it.
How can I pass my value in for that parameter?
JavaDoc to WebTarget.queryParam() should give you an answer to your problem. Basically you need to transform your code to something like:
target("foo/bar").queryParam("key", "blah").request().get()