We are trying to write a GetMapping in the Spring application, but it's not working as required. Below is the my code:
#RequestMapping("/app")
public class UserController {
#GetMapping("/embed/{numericId:^((?!e=.*).)*$}")
public String sendEmbedRedirect() {
//Code
}
}
So as per above code I am trying to allow and reject below type of URL:
Allowed: http://localhost:9001/app/embed/external/517331346b7357374a4b415038305770743555636c30516d2532466f464b2532467944356b4c4c6a7666306f385a51253344/a_p__Got&a_f__true/param_1
Not Allowed: http://localhost:9001/app/embed/external/517331346b7357374a4b415038305770743555636c30516d2532466f464b2532467944356b4c4c6a7666306f385a51253344/a_p__Got&a_f__true?e=L90
So as per above example only URL which does not contain "e" parameter should be allowed and URL with "e" parameter should be rejected.
Allowed link is accessed and interrupted by back-end.
Not Allowed link is accessed by Front-end directly.
I think you are mixing your path variable "/embed/{numericId}/{apSomething}" with an optional parameter (/the/path?optionalParam).
handleRequest(
#PathVariable String numericId,
#PathVariable String apSomething,
#RequestParam(value = "e", required = false) Boolean e
) {
if(e!=null) {
"return 400;?"
}
. . .
}
Related
I have a controller which will have 3 query strings.
Instead of having 3 fields in controller, I am defining them in class.
public class PassengerInformation{
String travellerAddress;
String travellerAge;
String travellerName;
}
Now in controller , I am able to accept them
#GetMapping("/passenger-info)
public TravelInformation getPassengerInfo(PassengerInformation info){
//Call a service
}
Now, this works as expected, if I pass the query string as is. eg: /passenger-info?travellerAge=21.
But, How do I accept the query parameter names different to it's corresponding fieldName.
I should be able to call it as below:
/passenger-info?traveller_age=21&traveller_name=JohnWick&traveller_address=ST.
Try to add the following constructor to your class
public class PassengerInformation{
String travellerAddress;
String travellerAge;
String travellerName;
#ConstructorProperties({"traveller_address", "traveller_age", "traveller_name"})
public PassengerInformation(String travellerAddress, String travellerAge, String travellerName) {
this.travellerAddress = travellerAddress;
...
}
}
The best you can do by the default features without any customisation is to use #ConstructorProperties :
public class PassengerInformation {
String travellerAddress;
String travellerAge;
String travellerName;
#ConstructorProperties({ "traveller_address", "traveller_age", "traveller_name" })
public PassengerInformation(String travellerAddress, String travellerAge, String travellerName) {
this.travellerAddress = travellerAddress;
this.travellerAge = travellerAge;
this.travellerName = travellerName;
}
}
This behaviour is mentioned at the docs as follows :
The #ModelAttribute parameter instance (i.e PassengerInformation)
is sourced in one of the following ways:
Retrieved from the model where it may have been added by a
#ModelAttribute method.
Retrieved from the HTTP session if the model attribute was listed in
the class-level #SessionAttributes annotation.
Obtained through a Converter where the model attribute name matches
the name of a request value such as a path variable or a request
parameter (see next example).
Instantiated using its default constructor.
Instantiated through a “primary constructor” with arguments that match
to Servlet request parameters. Argument names are determined through
JavaBeans #ConstructorProperties or through runtime-retained parameter
names in the bytecode.
The caveat here is that you need to make sure there are no default constructor in the PassengerInformation :
public class PassengerInformation {
public PassengerInformation(){}
}
I am working on a spring boot project. I am building a PUT/POST API which takes boolean data type in the request body. The issue is whenever I pass an integer value like 12,13,-15... etc it converts it to true (which is the default behavior of boolean), but my doubt is, is their a way to validate here such that it takes only 0,1, true and false. I tried adding the regex to validation.properties and #ESAPI annotations to my model, but I've read that data canonicalization happens before validation occurs. so I am stuck with this issue, How do I resolve this?
This is a PUT Request.
Here is the Request body
{
"access": true,
"prefLanguage": "English",
"prefTimeZone": "USA"
}
My Model is defined as
public class Consent implements Serializable {
#ESAPIPattern(validateWithPattern = "acess", required = true)
private String acess;
#JsonInclude(JsonInclude.Include.NON_NULL)
#ESAPIPattern(allowNull = true,validateWithPattern = "prefTimeZone")
private String prefTimeZone;
#JsonInclude(JsonInclude.Include.NON_NULL)
#ESAPIPattern(allowNull = true,validateWithPattern = "prefLanguage")
private String prefLanguage;
... getters and setters
}
Here I want "access" to accept only true|false or 0|1, I don't want it to accept any other integer value.
You can use a context validator which is provided by spring. You can create an annotation #BoolenValidator and put it in the request param or path variable.
public boolean isValid(String value, ConstraintValidatorContext context) {
boolean isValid = true;
if (isNull(value) || !(value==0|| value==1)) {
isValid = addViolation(value, "must be 0 or 1");
}
return isValid;
}
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.
}
I'm a newbie in java spring. I have checked for solutions online and I can't seem to get my hands on anything helpful.
I have a form that has been bound to an Entity, however, I have a field in that form that was not bound, which I receive as a requestParam.
So when the field is submitted I need to validate that parameter to ensure it's not empty.
#PostMapping("/store")
public String store(#Valid Quote quote,BindingResult result, Model model,#RequestParam(required=false) String [] tagsFrom) {
if(result.hasErrors()) {
model.addAttribute("jsontags", returnTagsAsJson(tagRepo.findAll()));
return "admin/quotes/create";
}
List<Tag> listtags = new ArrayList<Tag> ();
for(String tag : tagsFrom) {
Tag theTag = new Tag();
theTag.setTag(tag);
theTag.setCreatedAt(new Date());
theTag.setUpdatedAt(new Date());
if(tagRepo.findByTag(tag) == null) {
tagRepo.save(theTag);
}
listtags.add(tagRepo.findByTag(tag));
}
quote.setTags(listtags);
quoteRepo.save(quote);
return "redirect:/dashboard/quotes";
}
What I have tried;
I created a custom validation and added the annotation to the parameter but that gave an error "The annotation #EmptyArrayString is disallowed for this location"
public String store(#Valid Quote quote,BindingResult result, Model model,
#RequestParam(required=false) #EmptyArrayString String [] tagsFrom)
I have tried using #NotEmpty on the parameter which throws NullPointerException
I need a solution that allows me to display the error on the HTML form like this
<span th:if="${#fields.hasErrors('tags')}"
th:errors="${quote.tags}" class="errors">
</span>
So when the field is submitted I need to validate that parameter to ensure it's not empty.
,#RequestParam(required=false) String [] tagsFrom
By default, required is set to true. So, if the URL must have the param, you shouldn't do required=false.
String [] tagsFrom implies you expect a bunch of tag params. But, is it of the form http://localhost:xxx?param=1,2,3 or
http://localhost:xxx?param1=1¶m2="stringvalue" ?
For the first one, you can have the mapping method as:
public String store(...#RequestParam List<String> param)
For the second one, you can do:
public String store(...#RequestParam Map<String,String> allQueryParameters)
You can then do your necessary validations.
Read more here
We got this REST endpoint in which one of the field is mapped to a Boolean (The wrapper class) instance. We are using a Boolean instead of a boolean because design decision, so this is non-negotiable.
This Boolean value is mandatory and it must be specified by the sender ("whateverValue":"" should return a 400 error), but when arriving to the endpoint, the value is automatically converted to a correct false value.
So, the question is: Can this be done? Are we not understanding the contract of using a "Boolean" object instead of the primitive?
EDIT: Just to clarify, we are already validating "whateverValue":null, and the value can be either true or false, so, as far as I know, neither #NotNull or #AssertTrue/False can be used here.
If you want to validate the Object Type Boolean you should use #NotNull
Here is a question where this has been asked before.
I use #NotNull if a boolean is mendatory to be set ans #AssertTrue/false to verify the incoming value has a specific state.
Hope this helps
I coded your scenario as follows and it was ok!
Please correct me if my understanding (from your scenario) is incorrect.
Create DTO
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class TestDto {
#NotNull
Boolean testValue;
//getter and setter
}
Create Endpoint
#RestController
public class testController {
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void accountTrades(#RequestBody #Valid TestDto testDto) {
System.out.println(testDto.getTestValue());
}
}
Using Postman, when I send testValue:"", it throws a notnull exception, that means the server has received null, and there is no null to false conversion. (It worked correctly!)
Server response is as follows