Front slash and special characters in rest service url [duplicate] - spring-boot

I need to handle requests as following:
www.example.com/show/abcd/efg?name=alex&family=moore (does not work)
www.example.com/show/abcdefg?name=alex&family=moore (works)
www.example.com/show/abcd-efg?name=alex&family=moore (works)
It should accept any sort of character from the value that is located between www.example.com/show/ and ?. Please note the value that would be located there would be a single value not name of an action.
For example: /show/abcd/efg and /show/lkikf?name=Jack in which the first request should redirect user to the page abcd/efg (because thats a name) and the second one should redirect user to the page lkikf along with value of parameter name.
I have following controller to handle it but the issue is when I have / in the address the controller is unable to handle it.
#RequestMapping(value = "/{mystring:.*}", method = RequestMethod.GET)
public String handleReqShow(
#PathVariable String mystring,
#RequestParam(required = false) String name,
#RequestParam(required = false) String family, Model model) {
I used following regex which did not work.
/^[ A-Za-z0-9_#./#&+-]*$/

Another way I do is:
#RequestMapping(value = "test_handler/**", method = RequestMethod.GET)
...and your test handler can be "/test_hanlder/a/b/c" and you will get the whole value using following mechanism.
requestedUri = (String)
request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);

You have to create two methods then one having the #RequestMapping(value = { "/{string:.+}" }) annotation and the other having #RequestMapping(value = { "/{string:.+}", "/{string:.+}/{mystring:.+}" }) and then act accordingly in each, because you can't have optional path variables.
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
#Controller
#RequestMapping("/show")
public class HelloController {
#RequestMapping(value = { "/{string:.+}" })
public String handleReqShow(#PathVariable String string,
#RequestParam(required = false) String name,
#RequestParam(required = false) String family, Model model) {
System.out.println(string);
model.addAttribute("message", "I am called!");
return "hello";
}
#RequestMapping(value = { "/{string:.+}", "/{string:.+}/{mystring:.+}" })
public String whatever(#PathVariable String string,
#PathVariable String mystring,
#RequestParam(required = false) String name,
#RequestParam(required = false) String family, Model model) {
System.out.println(string);
System.out.println(mystring);
model.addAttribute("message", "I am called!");
return "hello";
}
}

The first one is not working because you are trying to handle an entirely new URL which is not actually mapped your controller.
www.example.com/show/abcd/efg?name=alex&family=moore (does not work)
The correct mapping for the above URL could be like the below code.
#RequestMapping(value = {"/{mystring:.*}" , "/{mystring:.*}/{mystring2:.*}"}, method = RequestMethod.GET)
public String handleReqShow(
#PathVariable String mystring,
#PathVariable String mystring2,
#RequestParam(required = false) String name,
#RequestParam(required = false) String family, Model model) {
I have tried the similar concept when my one controller is used to handle multiple types of request.

You could encode slashes on UI with %2f: http://www.example.com/show/abcd%2fefg?name=alex&family=moore.
Now you should configure Spring to handle slashes. Simple config example:
#RestController
public class TestController {
#GetMapping("{testId:.+}")
public String test(#PathVariable String testId) {
return testId;
}
#GetMapping("{testId:.+}/test/{messageId}")
public String test2(#PathVariable String testId, #PathVariable String messageId) {
return testId + " " + messageId;
}
//Only if using Spring Security
#Configuration
public static class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
DefaultHttpFirewall firewall = new DefaultHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}
}
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public static class SpringMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setUrlDecode(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
}

The default Spring MVC path mapper uses the / as a delimiter for path variables, no matter what.
The proper way to handle this request would be to write a custom path mapper, that would change this logic for the particular handler method and delegate to default for other handler methods.
However, if you know the max possible count of slashes in your value, you can in fact write a handler that accepts optional path variables, and than in the method itself, assemble the value from path variable parts, here is an example that would work for max one slash, you can easily extend it to three or four
#RequestMapping(value = {"/{part1}", "/{part1}/{part2}"}, method = RequestMethod.GET)
public String handleReqShow(
#PathVariable Map<String, String> pathVariables,
#RequestParam(required = false) String name,
#RequestParam(required = false) String family, Model model) {
String yourValue = "";
if (pathVariables.containsKey("part1")) {
String part = pathVariables.get("part1");
yourValue += " " + part;
}
if (pathVariables.containsKey("part2")) {
String part = pathVariables.get("part2");
yourValue += " /" + part;
}
// do your stuff
}
You can catch all the path variables inside the map, the map #PathVariable Map<String, String> pathVariables, but the downside is that the static part of the mapping has to enumarate all the possible variations

You can define rules to avoid that
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
rules.xml add this to your WEB-INF
<urlrewrite>
<rule>
<from>^/(10\..*)$</from> <!-- tweak this rule to meet your needs -->
<to>/Show?temp=$1</to>
</rule>
</urlrewrite>

Try escaping forward slash.
Regex: /^[ A-Za-z0-9_#.\/#&+-]*$/

Related

How to get a param from URL Rest Service that is after a question mark (?)

I have the following String that I receive in my rest service:
.6.75:5050/pretups/C2SReceiver?REQUEST_GATEWAY_CODE=8050122997&REQUEST_GATEWAY_TYPE=EXTGW&LOGIN=CO8050122997_EXTGW&PASSWORD=89b87741ca3f73b0b282ae165bad7501&SOURCE_TYPE=XML&SERVICE_PORT=190
I have the following code:
#Controller
public class servicioscontroller {
#RequestMapping(value = "/pretups/{p1}?{trama}", method = RequestMethod.GET)
#ResponseBody
public String enviarTrama(#PathVariable("p1") String p1,#PathVariable("trama") String trama){
return p1+trama;
}
}
And im getting this result:
C2SReceive
I need also the string after that ?, what im doing wrong or how do I get that? thanks
You should use #RequestParam:
#Controller
public class servicioscontroller {
#RequestMapping(value = "/pretups/{p1}", method = RequestMethod.GET, params = ["trama"])
#ResponseBody
public String enviarTrama(
#PathVariable("p1") String p1,
#RequestParam(name = "trama", required = "true") String trama
){
return p1+trama;
}
}
Note that you have to put request params in the #RequestMapping's params attribute instead of the path string.
The difference between #RequestParam and #PathParam is described in this answer.

Spring Boot - How can I pass custom values in HTTP Post api?

I'm new with Spring Boot and I have difficult to understand how can I pass data. For example:
I want pass those data to my server:
{
"code", 1,
"name": "C01"
}
So I have create always a custom Object with code and name as attributes to have this http post api?
#RequestMapping(value = "/new/", method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#RequestBody CustomObject customObject){
...
}
Another solution I see that can be this but I can't pass numbers (int code), right?
#RequestMapping(value = "/new/{code}/{name}", method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#PathVariable("code") int code, #PathVariable("name") String name) {
...
}
Kind regards :)
You can pass code and name as PathVariables just like in your example:
#RequestMapping(value = "/new/{code}/{name}")
public ResponseEntity<?> createOrder(#PathVariable("code") int code, #PathVariable("name") String name) {
...
}
A PathVariable can be an int or a String or a long or a Date, according to the docs:
A #PathVariable argument can be of any simple type such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a TypeMismatchException if it fails to do so.
You could also define a PathVariable of type Map<String, Object> like this:
#RequestMapping(value = "/new/{code}/{name}")
public ResponseEntity<?> createOrder(#PathVariable("map") Map<String, Object> map) {
Integer code = (Integer) map.get("code");
String name = (String) map.get("name");
...
}
You could even use #RequestParam and supply the data in the form of URL query parameters.
So, there are numerous ways in which data can be passed to a Spring MVC controller (more details in the docs) but I think the convention for posting complex data (by "complex" I mean more than a single piece of state) is to define a request body which contains a serialised form of that complex state i.e. what you showed in the first example in your queston:
#RequestMapping(value = "/new/", method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#RequestBody CustomObject customObject){
...
}
If this question is about RESTful best practice, since you are developing webservice for creating an Order object, this is how I would design it
Order.java
public class Order {
private Integer code;
private String name;
public Integer getCode() {
return code;
}
public void setCode(final Integer code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
Controller
#RequestMapping(value = "/orders", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Order> createOrder(#Valid #RequestBody Order order){
...
}
Technically, you can do many things to achieve the same thing, but that will not be a RESTful service, it will be an RPC at best.

404 Request Resource not found

I am using Spring Framework with restful web services, and I am trying to create an API with restful service and use a get method. I have created a method and I'm trying to have it return a string, but instead I get a 404 error - requested resources not found. Please see my code below:
#RestController
#RequestMapping("/test")
public class AreaController {
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return "list";
}
}
I am using: localhosr:8080/MyProject/wangdu
This error occurs because you forgot to add
#RequestMapping(value = "/{name}", method = RequestMethod.GET) before your find method:
#RestController
#RequestMapping("/test")
public class AreaController {
#RequestMapping(value = "/{name}", method = RequestMethod.GET)
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return "list";
}
}
Please make sure about this:
The value that the find method is returning is a String with the value "list" and the find method declaration is waiting for a RestResponse object
For example if I have a RestResponse object like this:
public class RestResponse {
private String value;
public RestResponse(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
}
Then try to return the value in this way:
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return new RestResponse("list");
}
Verify that the method has #RequestMapping annotation with the value that your expect from the url
#RequestMapping(method = RequestMethod.GET, value = "/{name}")
By default the proper way to call the rest resource is by the #RequestMapping value that you set at the #RestController level (#RequestMapping("/test")), in this case could be: http://localhost:8080/test/myValue
If you need to use a different context path then you can change it on the application.properties (for spring boot)
server.contextPath=/MyProject/wangdu
In that case you can call the api like this:
http://localhost:8080/MyProject/wangdu/test/myValue
Here is the complete code for this alternative:
#RestController
#RequestMapping("/test")
public class AreaController {
#RequestMapping(method = RequestMethod.GET, value = "/{name}")
public RestResponse find(#PathVariable String name, ModelMap model) {
model.addAttribute("movie", name);
return new RestResponse("list");
}

spring boot : RequestMapping

I have following three REST API methods :
#RequestMapping(value = "/{name1}", method = RequestMethod.GET)
public Object retrieve(#PathVariable String name1) throws UnsupportedEncodingException {
return configService.getConfig("frontend", name1);
}
#RequestMapping(value = "/{name1}/{name2}", method = RequestMethod.GET)
public Object retrieve(#PathVariable String name1, #PathVariable String name2) throws UnsupportedEncodingException {
return configService.getConfig("frontend", name1, name2);
}
#RequestMapping(value = "/{name1}/{name2}/{name3}", method = RequestMethod.GET)
public Object retrieve(#PathVariable String name1, #PathVariable String name2, #PathVariable String name3) {
return configService.getConfig("frontend", name1, name2,name3);
}
getConfig method is configured to accept multiple parameters like:
public Object getConfig(String... names) {
My Question is : is it possible to achieve the above RequestMapping using only one method/RequestMapping ?
Thanks.
Simple approach
You can use /** in your mapping to grab any URL and then extract all parameters from the mapping path. Spring has a constant which allows you to fetch the path from the HTTP request. You just have to remove the unnecessary part of the mapping and split the rest to get the list of parameters.
import org.springframework.web.servlet.HandlerMapping;
#RestController
#RequestMapping("/somePath")
public class SomeController {
#RequestMapping(value = "/**", method = RequestMethod.GET)
public Object retrieve(HttpServletRequest request) {
String path = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString();
String[] names = path.substring("/somePath/".length()).split("/");
return configService.getConfig("frontend", names);
}
}
Better approach
However, path variables should be rather used for identifying resources in your application and not as a parameters to a given resource. In that case, it is advised to stick with simple request parameters.
http://yourapp.com/somePath?name=value1&name=value2
You mapping handler would look much more simple:
#RequestMapping(method = RequestMethod.GET)
public Object retrieve(#RequestParam("name") String[] names) {
return configService.getConfig("frontend", names);
}
You should probably use #RequestParam instead and method POST in order to achieve what you want.
#RequestMapping(name = "/hi", method = RequestMethod.POST)
#ResponseBody
public String test(#RequestParam("test") String[] test){
return "result";
}
And then you post like that:
So your array of Strings will contain both values
Also in REST a path corresponds to a resource, so you should ask yourself "what is the resource i am exposing ?". It would probably be something like /config/frontend and then you specify your options through request params and/or HTTP verbs
You can retrieve the complete path with request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE) and then parse it to get all the values.
This should work:
#SpringBootApplication
#Controller
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping(value ={"/{name1}","/{name1}/{name2}","/{name1}/{name2}/{name3}"})
public #ResponseBody String testMethod(
#PathVariable Map<String,String> pathvariables)
{
return test(pathvariables.values().toArray(new String[0]));
}
private String test (String... args) {
return Arrays.toString(args);
}
}

Spring REST Service Controller not being validate by #PathVariable and #Valid

#Controller
#EnableWebMvc
#Validated
public class ChildController extends ParentController<InterfaceController> implements InterfaceController{
#Override
#RequestMapping(value = "/map/{name}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public List<Friends> getAllFriendsByName(
#Valid
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable("name") String name,
#RequestParam(value="pageSize", required=false) String pageSize,
#RequestParam(value="pageNumber", required=false) String pageNumber,
HttpServletRequest request) throws BasicException {
//Some logic over here;
return results;
}
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
Hi, I am trying to do pretty basic validation for a spring request parameter but it just doesn't seem to call the Exception handler, could someone point me into the right direction
P.S. I keep getting NoHandlerFoundException
Spring doesn't support #PathVariable to be validated using #Valid. However, you can do custom validation in your handler method or if you insist on using #Valid then write a custom editor, convert your path variable value to an object, use JSR 303 bean validation and then use #Valid on that object. That might actually work.
Edit:
Here's a third approach. You can actually trick spring to treat your path variable as a model attribute and then validate it.
1. Write a custom validator for your path variable
2. Construct a #ModelAttribute for your path variable and then use #Validator (yes not #Valid as it doesn't let you specify a validator) on that model attribute.
#Component
public class NameValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
String name = (String) target;
if(!StringUtils.isValidName(name)) {
errors.reject("name.invalid.format");
}
}
}
#RequestMapping(value = "/path/{name}", method = RequestMethod.GET)
public List<Friend> getAllFriendsByName(#ModelAttribute("name") #Validated(NameValidator.class) String name) {
// your code
return friends;
}
#ModelAttribute("name")
private String nameAsModelAttribute(#PathVariable String name) {
return name;
}

Resources