Optional path-parameter in jersey 2.6 sub-resource without constant-part-path - rest

I have 2 almost identical sub-resources:
#GET
#Path("/{device}/{property}")
#Produces(MediaType.APPLICATION_JSON)
public Response getParameterValue(#PathParam("device") final String device,
#PathParam("property") final String property) {
...
}
and
#GET
#Path("/{device}/{property}/{field}")
#Produces(MediaType.APPLICATION_JSON)
public Response getFieldValue(#PathParam("device") final String device,
#PathParam("property") final String property,
#PathParam("field") final String field ) {
...
}
that I want to replace with a single one using regular expressions. I saw among other related many articles about this for example this post and this one on SO.
So I was almost sure to achive my goal writting the sub-resource like so:
#GET
#Path("/{device}/{property}{p:/?}{field:([a-zA-Z][a-zA-Z0-9_]*)?}")
#Produces(MediaType.APPLICATION_JSON)
public Response getParameterOrFieldValue(#PathParam("device") final String device,
#PathParam("property") final String property,
#PathParam("field") final String field ) {
...
}
Unfortunately if a value is present for the field path-parameter:
http://localhost:8080/my-rest/v1/device001/property001/field001
, I got:
HTTP 405 Method Not Allowed
But if there is no value for field:
http://localhost:8080/my-rest/v1/device001/property001
it works fine.
I than introduced a constant part 'just2C' in the path of the sub-resource:
#GET
#Path("/just2C/{device}/{property}{p:/?}{field:([a-zA-Z][a-zA-Z0-9_]*)?}")
#Produces(MediaType.APPLICATION_JSON)
public Response getParameterOrFieldValue(#PathParam("device") final String device,
#PathParam("property") final String property,
#PathParam("field") final String field ) {
...
}
it works in both cases: with or without a value for the path-parameter 'field'.
I'd like to undestand why?
I am using jersey2.6

Related

Optional #Pathvariable in REST controller spring 4

I'm writing a Rest Service (HTTP Get endpoint), where in the below uri does the following
http://localhost:8080/customers/{customer_id}
fetch the details for the customer_id passed in the uri
if the customer_id is not passed (http://localhost:8080/customers), fetch all the customers details.
Code:
#RequestMapping(method = RequestMethod.GET, value = "customers/{customer_id}")
public List<Customer> getCustomers(
#PathVariable(name = "customer_id", required = false) final String customerId) {
LOGGER.debug("customer_id {} received for getCustomers request", customerId);
}
However, with the above code, for the second scenario control is flowing to getCustomers().
Note: I'm using Java8 and spring-web 4.3.10 version
Highly appreciate any help on this.
Optional #PathVariable is used only if you want to map both GET /customers/{customer_id} and GET customers into single java method.
You cannot send request which will be sent to GET /customers/{customer_id} if you don't send customer_id.
So in your case it will be:
#RequestMapping(method = RequestMethod.GET, value = {"/customers", "customers/{customer_id}"})
public List<Customer> getCustomers(#PathVariable(name = "customer_id", required = false) final String customerId) {
LOGGER.debug("customer_id {} received for getCustomers request", customerId);
}
public abstract boolean required
Whether the path variable is required.
Defaults to true, leading to an exception being thrown if the path variable is missing in the incoming request. Switch this to false if you prefer a null or Java 8 java.util.Optional in this case. e.g. on a ModelAttribute method which serves for different requests.
You can use null or Optional from java8
This may help someone that is trying to use multiple optional path variables.
If you have more than one variable, you can always accept multiple paths.
For instance:
#GetMapping(value = {"customers/{customerId}&{startDate}&{endDate}",
"customers/{customerId}&{startDate}&",
"customers/{customerId}&&{endDate}",
"customers/{customerId}&&"
})
public Customer getCustomerUsingFilter(#PathVariable String customerId, #PathVariable Optional<Date> startDate, #PathVariable Optional<Date> endDate)
Then you would call this URL using all the path separators (in this case &)
Like GET /customers/1&& or
GET /customers/1&&2018-10-31T12:00:00.000+0000 or
GET /customers/1&2018-10-31T12:00:00.000+0000& or
GET /customers/1&2018-10-31T12:00:00.000+0000&2018-10-31T12:00:00.000+0000
You should create two end-point here to handle the individual request :
#GetMapping("/customers")
public List<Customer> getCustomers() {
LOGGER.debug("Fetching all customer");
}
#GetMapping("/customers/{id}")
public List<Customer> getCustomers(#PathVariable("id") String id) {
LOGGER.debug("Fetching customer by Id {} ",id);
}
#GetMapping is equivalent to #RequestMapping(method = RequestMethod.GET) and #GetMapping("/customers/{id}") is equivalent to #RequestMapping(method = RequestMethod.GET, value = "customers/{id}")
Better approach would be like this :
#RestController
#RequestMapping("/customers")
public class CustomerController {
#GetMapping
public List<Customer> getAllCustomers() {
LOGGER.debug("Fetching all customer");
}
#GetMapping("/{id}")
public Customer getCustomerById(#PathVariable("id") String id) {
LOGGER.debug("Fetching customer by Id {} ",id);
}

Spring MVC #RequestParam default value not resolved

I have this method:
#GetMapping(value = "warehouses/{warehouseId}/issues", headers = BOOTGRID_REQUEST)
public ResponseEntity<BootgridResponse<T>> listWarehouseIssues(
#PathVariable final long warehouseId,
#RequestParam(required = false, defaultValue = ISSUE_STATE_ALL) final String state,
#Valid #ModelAttribute final BootgridRequest request,
final BindingResult bindingResult
)
Worked great in all cases until I registered a new filter that wraps the request into a wrapper.
#Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(crossScriptingFilter());
registration.addUrlPatterns("/*");
registration.setName("XFilter");
registration.setOrder(1);
return registration;
}
#Bean
public Filter crossScriptingFilter() {
return new CrossScriptingFilter();
}
Since, the argument "state" is given null when not specified in the request params.
When I remove the filter registration, I get the default value instead of null.
I am missing a piece here, I don't get why the default value wouldn't be given ?
I think that the "value" property is missing. This value represent the name of the request param:
#RequestParam(value = "state", required = false, defaultValue = ISSUE_STATE_ALL) final String state
It seems I had environment issues, other services also had default values but was provided.
After a while, it seems all was back to normal and working again.
This question could also be deleted, since it provides nothing concrete to readers.

BSON ObjectID (Morphia) representation in Typescript/Angular2 Project

I have a Java class representing a customer with some properties like name, adress etc.
And I have the property:
#Id
#Property("_id")
private ObjectId id;
The customer will be fetched from a mongoDB. Everything is fine and all properties are filled.
Then I try to transport data via REST to a angular2 client
I have a Customer representation in typescript
export class Customer {
public id: string <---
Mapping inside rest call in client
.map((response: Response) => <Customer> response.json())
what do I need to put here, that the property will be mapped and that I can use it within the angular client.
I've tried to install bson-objectid via npm, but I have no idea how to get it to map the id property. All the others are working fine.
Solution found!
Create a
public YourAdapterName extends XMLAdapter<String, ObjectID> {
#Override
public String marshal(ObjectId v) throws Exception {
return v.toString();
}
#Override
public ObjectId unmarshal(String v) throws Exception {
return new ObjectId(v);
}
}
This Adapter returns the string representation of the ObjectID and I can use
id: string
in Typescript.

Include null field values in Jersey REST Response

I'm including Instance of Following class in one of my REST Resource Response.
public class GenericResponse<T> implements Serializable {
private String message;
private String errorMessage;
private String errorCode = "0";
....
}
There are other fields in this class but, when a field is null or "0" (as a String), they are being omitted in the Client's Response.
How to include all fields in the Client's REST response even though they are null/empty/0
Additional Information:
Jersey Version: 2.6
I solved this with the Annotation
#JsonInclude(Include.ALWAYS)
on
GenericResponse

Spring boot REST application

I am trying to make a RESTful application in Java using Spring boot by following the tutorial here. I want to modify it so that I can extract an identifier from the URL and use it to serve requests.
So http://localhost:8080/members/<memberId> should serve me a JSON object with information about the member whose ID is <memberId>. I don't know how to
Map all http://localhost:8080/members/* to a single controller.
Extract the from the URL.
Should the logic of extracting the memberId and using it be part of the controller or a separate class, as per the MVC architecture?
I am new to Spring/Spring-boot/MVC. It is quite confusing to get started with. So please bear with my newbie questions.
Map all http://localhost:8080/members/* to a single controller.
You can use a placeholder in a request mapping to so it'll handle multiple URLs. For example:
#RequestMapping("/members/{id}")
Extract the id from the URL
You can have the value of a placeholder injected into your controller method using the #PathVariable annotation with a value that matches the name of the placeholder, "id" in this case:
#RequestMapping("/members/{id}")
public Member getMember(#PathVariable("id") long id) {
// Look up and return the member with the matching id
}
Should the logic of extracting the memberId and using it be part of the controller or a separate class, as per the MVC architecture?
You should let Spring MVC extract the member id from the URL as shown above. As for using it, you'll probably pass the URL to some sort of repository or service class that offers a findById method.
As you can see in the code below, service for customer are in one controller to get one and to add new customer.
So, you will have 2 services:
http://localhost:8080/customer/
http://localhost:8080/customer/{id}
#RestController("customer")
public class SampleController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Customer greetings(#PathVariable("id") Long id) {
Customer customer = new Customer();
customer.setName("Eddu");
customer.setLastname("Melendez");
return customer;
}
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public void add(#RequestBody Customer customer) {
}
class Customer implements Serializable {
private String name;
private String lastname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getLastname() {
return lastname;
}
}
}