Is it a good practice to store the query parameters as static fields to handle a request? For instance, my endpoint accepts two query parameters. To validate and process the request body, I have to pass these query parameters to all the low-level methods (like daisy-chaining). To avoid this, I can store these parameters as static fields within a static class and access these parameters whenever required instead of passing it around. I am fairly new to REST development and I don't want to reinvent the wheel if there are existing patterns to handles the problem I facing. Any thoughts on my approach?
In the code below, I am passing query1, query2, query3 parameters to validateRequest API, which is again passing some of the query parameters to its internal methods. This daisy-chaining will continue to low-level methods. My question is if store these query parameters in a static class, I don't have to pass the query parameters around. When an API requires query parameters, it can call the static class to access the parameters.
Pseudocode:
public class Temp {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response post(
#QueryParam("query1") final String query1,
#QueryParam("query2") final String query2,
#QueryParam("query3") final String query3,
final Object requestBody) {
validateRequest(requestBody,query1,query2,query3)
return Response.status(Status.OK).build();
}
private void validateRequest(Object requestBody, String query1, String query2, String query3) {
validateFirstPartOfRequest(requestBody,query1);
validateSecondPartOfRequest(requestBody,query1,query2);
validateThirdPartOfRequest(requestBody,query1),query3;
}
}
To validate and process the request body, I have to pass these query parameters to all the low-level methods I'm not affraid by this. For instance request parameters will be used for filtering a collection ressource so it sounds logic to pass these parameters until the data access layer for applying your filters at database-level (criteria query).
If you have lot of parameters just wrap them into a parameter object (https://refactoring.guru/introduce-parameter-object)
Regarding static it is really a bad idea. Static data will be shared by all requests(threads) coming to your controller and so you'll face some overlap between values.
It's not recommended to change a static variable from multiple threads just to avoid carrying it in the class instance, but if that's what you want to do, maybe you should have a look at ThreadLocal.
Related
In my application, there is a requirement of getting data based on some parameters.
I just want to what is the better way to do.
The one way is, I can pass the list of parameters as a path variable.
The second way is, I can pass the request body, I think it is vague and I am not sure it is possible or not.
You can find the code below:
#GetMapping("/cities/{cityName}/latitude/{latitude}/longitude/{longitude}/cityId/{cityId}/street/{street}")
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude() {
}
I just want to know how can I achieve the same.
There is one more question, E-commerce companies have big filter criteria so how they are achieving.
Although there is no hard & fast rule but I generally avoid sending a body in GET request because it's a bad design. You should also refer to this SO Post which contains discussion about using body in GET request. It's an opinionated post and there is no clear YES or NO, but you will get an idea.
HTTP GET with request body
You can either use Path params or query params depending on what those field represent.
Regarding the difference or which to use when I am quoting this answer, which mentions that although there is no hard rule but generally it's better to use params which can uniquely identify the resource as Path param (e.g. id, name etc) and if your param is supposed to do something like filtering/sorting e.g. records after Jan 1 2019 , then go for query param.
Also personally in one of my APIs (which performs filtering), I am using a generic query param, where I pass on JSON object in my query. So basically my API needs to search an object based on variable/multiple attributes. E.g. I have in my db , objects which have certain voltage, current, size etc. values. So, request might come with a combination of 1 or more. So to keep my API flexible, I have provided a query param which can accept JSON object.
So I make a request like this:
{{SERVER}}/api/search?query={voltage:12V,size:10}
And in my API, I can convert this json object to corresponding POJO:
#GET
#Path("/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response search(#QueryParam("query") String queryParam) throws Exception
{
Myobj obj = new Gson().fromJson(queryParam, Myobj.class);
// rest of code
By passing the parameters in the path, you are restricting yourself to extend your API. If you want to extend your API, for example, if you want to filter with criteria as Street1 (or) Street2 then your path wouldnot support it and it will force you to update your API. It is better to pass criteria objects in the body or url parameter. Amazon India is passing criteria like below. I have choosen mobiles with criteria as Manufacturer = Samsung or MI, Storage as 8gb or 4gb and they simply appended the criteria in the query parameters.
There is a third way, Request Params.
#GetMapping
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude(#RequestParam("cityName") String cityName, #RequestParam("latitude") String latitude, #RequestParam("longitude") String longitude){
// Your code
}
For more: 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Parameters using this annotation are required by default, but you can specify that a parameter is optional by setting #RequestParam's required attribute to false (e.g., #RequestParam(value="id", required=false)).
https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#mvc-ann-requestparam
I have a resource class and within it a #GET that takes one query param called operation (this should be static) and then I want to take a variable number of other query params that can be named anything.
My first thought was to do something like this:
public Response get(
#QueryParam("operation") String operation,
#QueryParam("list") final List<String> list) {
//do stuff
}
The problem here is that I would have to make a request like:
...?operation=logging&list=ABC&list=XYZ
While what I want is to be able to have something like this:
...?operation=logging&anything=ABC&something_else=XYZ
Is there a way to make the list query param #QueryParam(//anything)?
In doing some information gathering I ran across this sort of approach:
#GET
public void gettest(#Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
String operation = queryParams.getFirst("operation");
for (String theKey : queryParams.keySet()) {
System.out.println(queryParams.getFirst(theKey));
//do stuff with each other query param
}
}
Is multivaluedmap the way to go for this situation -- Or is there a way to use a variable query param name? Or a better approach? Thanks.
Edit/Update:
This is using javax.ws.rs
The use case is: this application being used as a tool for mocking responses (used for testing purposes in other applications). The mock responses are retrieved from a DB by looking up the 'operation' and then some sort of 'id'. The actual id used could be any of the "list" query params given. The reason for this is to give flexibility in different applications to use this mock service -- the urls in applications may be constructed many different ways and this makes if so one doesn't have to change around their code to be able to use the mock service.
As in this question, use a map:
#RequestMapping(value = {"/search/", "/search"}, method = RequestMethod.GET)
public String search(
#RequestParam Map<String,String> allRequestParams, ModelMap model) {
return "viewName";
}
I overwrote the public Action onRequest(final Http.Request request, Method method) method to check that the mandatory http headers are passed in and valid, i.e.: I extract the apiKey (and other things) and make sure that these are valid (that there's data associated with the apiKey). Then I call return super.onRequest(request, method); and I end up in my controller where I once again have to extract the apiKey and get the associated data from the DB.
Is there a way to pass in the data to my controller's method (for instance: public static Result addUser() ).
Thank you.
I know this question is for Java, but I work with Play in Scala, and this question is more related to Play then it is to Java
In Scala I am able to extend WrappedRequest to make my own custom request type, that has instance variables I want to access in my controller:
// Scala code
case class MyRequest[A](request: Request[A]) extends WrappedRequest(request) {
// This is a public instance variable
val apiKey = request.headers.get("Authorization)
}
Then later on in my controller I can access the action object which now has the type of MyRequest:
// Scala code
def foobar(action: MyRequest[AnyContent]) = Action {
// Do something with the api key
val apiKey = action.apiKey
// Send back a response
Ok("foobar")
}
In Java it looks like you can do something similar using a Wrapped Context
Using the Retrofit REST Client library from Square, is there anyway of providing default/constant values for POST/PUT fields in a call.
I know about including constant query parameters by simply including them in the path, but this work for Body parameters.
I have an API that looks similar to:
POST /api/create
type=constanta&value={value}
POST /api/create
type=constantb&value={value}&otherValue={otherValue}
where the second variant requires an additional argument to be supplied. Rather than having a single java method that took all three arguments, I was hoping to be able to elide the constants from the method call, something like:
create(String value);
create(String value, String otherValue);
and have retrofit inject the type argument constant.
Given that adding #FormUrlEncoded can be added to modify how the body is encoded, if it's not natively supported by Retrofit, is there anyway of adding my own annotation and injecting such default values? ( It doesn't appear that RequestInterceptor allows one to modify the body.. ).
Maybe one option would be to send an object, which encapsulates all your values, instead of all string values separately? The object would implement your default values.
For example, you could create a class:
public class CreateObject {
private String type = "constant";
private String value;
private String otherValue;
public CreateObject(String value, String otherValue) {
this.value = value;
this.otherValue = otherValue;
}
}
Your class handles your constant. You could just set it to a default value "constant", like I did above, or set it on the fly in the constructor.
Now all you've to do is to create the object with the values and make the request with Retrofit. Instead of using the string values directly, just pass the object. Your interface could look like this:
public interface CreateService {
#POST("/api/create")
void create(#Body CreateObject create, Callback<CreateObject> cb);
}
The request implementation like this:
CreateObject create = new CreateObject("value", "otherValue");
createService.create(create, new Callback<CreateObject)() {…});
This should include all three of your values in the request body, if they are set. If a value is null, it won't be included in the request body. Based on your two examples above, you would now only need one interface method. Which values are sent is based on the createObject you pass on. For example, if you set otherValue as null, it won't be part of the request body.
My examples were modified from: https://futurestud.io/blog/retrofit-send-objects-in-request-body/
Is it possible for you to use Guava or Java 8 Optional as second argument in method? Then if that argument will be absent you can just ignore it
I have written a web-service application that has in a main class generated random value per request (for logging).
I cannot set it as a static field because next request will override it.
I also cannot pass it to the every class that I use in the main one (as an argument or with setter).
Is it possible to create some semi-static field - visible for one request but not for every other that go to the web-service ?
You can safely assume that, in the Java EE model, each single request is served by a single thread and that there is no contention by concurrent requests.
Having said that, you can employ a Singleton using a ThreadLocal, let the Servlet populate the value and have the underlying classes access the sigleton without having notion of the threads or the HTTP request context:
public class RandomValueHolder {
private static ThreadLocal<Long> randomValue;
public static Long getRandomValue() {
return randomValue.get();
}
public static void setRandomValue(Long value) {
randomValue = new ThreadLocal<Long>();
randomValue.set(value);
}
}
Why not use HttpRequest and store the value as attribute
Save the data in the request itself with Request.setAttribute() and use the corresponding Request.getAttribute() to retrieve it.