Skip to content
Advertisement

Add prefix or suffix to the GET,PUT,POST,DELETE mappings in controller at method level using custom annotation – Spring REST Controller

I am trying to add a prefix or suffix in the controller at the method level on top of GET, POST, PUT, DELETE mappings.

Controller class

@RestController
@RequestMapping("/something")
public class SomeController {
@PutMapping("/some/path/")
public ResponseEntity<ApiResponse<String>> someMethod() {
....
}
...
}

So, basically, the above request URL should be something like : http://localhost:8080/something/some/path/

Now, I just want to add some prefix or suffix whatever is feasible to the request URL which will be something like : http://localhost:8080/something/read/some/path/ or http://localhost:8080/something/some/path/read/ the extra “/read” which needs to be added to the request URL as a prefix or suffix. I can do this directly by adding this to the PutMapping value, but I want to decorate it somewhat using annotation like @Read

So, the updated Controller class will be like

@RestController
@RequestMapping("/something")
public class SomeController {
@Read
@PutMapping("/some/path/")
public ResponseEntity<ApiResponse<String>> someMethod() {
....
}
...
}

and the same way updated request URL will be like : http://localhost:8080/something/read/some/path/

I am unable to find a better way to do this. Till now I have only achieved adding a class-level prefix using custom annotation.

Can anyone please help with the above requirement.? Thank you !!

I am also curious to know whether is achievable or not even?

Advertisement

Answer

Using such way of path extension make your code less understandable. (maybe you should read more about RESTful API) But spring can do almost everything you want.

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.stream.Stream;

@SpringBootApplication
public class DemoApplication {

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Read {
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Write {
    }

    @Bean
    public WebMvcRegistrations webMvcRegistrations() {
        return new WebMvcRegistrations() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new RequestMappingHandlerMapping() {
                    @Override
                    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
                        RequestMappingInfo defaultRequestMappingInfo = super.getMappingForMethod(method, handlerType);
                        if (defaultRequestMappingInfo == null) {
                            return null;
                        }
                        String pathSuffix;
                        if (method.isAnnotationPresent(Read.class)) {
                            pathSuffix = "read";
                        } else if (method.isAnnotationPresent(Write.class)) {
                            pathSuffix = "write";
                        } else {
                            return defaultRequestMappingInfo;
                        }
                        //extend path by mutating configured request mapping info
                        RequestMappingInfo.Builder mutateBuilder = defaultRequestMappingInfo.mutate();
                        mutateBuilder.paths(
                                defaultRequestMappingInfo.getPatternValues().stream()
                                        .map(path -> path + "/" + pathSuffix)
                                        .toArray(String[]::new)
                        );
                        return mutateBuilder.build();
                    }
                };
            }
        };
    }

    @RestController
    @RequestMapping("/books")
    public static class BooksController {

        @Read
        @GetMapping("/{id}")
        public String readBook(@PathVariable("id") String bookId) {
            return bookId;
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

Extension point is here, you can change path like you want.

Example:

request: http://localhost:8080/books/asd

response: 404

output:

2022-06-27 10:49:48.671 DEBUG 8300 --- [nio-8080-exec-2] com.example.demo.DemoApplication$1$1     : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)

request: http://localhost:8080/books/asd/read

response: asd

output:

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _    
( ( )___ | '_ | '_| | '_ / _` |    
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |___, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.0)

2022-06-27 10:48:53.622  INFO 8300 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_312 on DESKTOP with PID 8300 ()
2022-06-27 10:48:53.624 DEBUG 8300 --- [           main] com.example.demo.DemoApplication         : Running with Spring Boot v2.7.0, Spring v5.3.20
2022-06-27 10:48:53.625  INFO 8300 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2022-06-27 10:48:54.227  INFO 8300 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-06-27 10:48:54.233  INFO 8300 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-06-27 10:48:54.233  INFO 8300 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.63]
2022-06-27 10:48:54.298  INFO 8300 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-06-27 10:48:54.298  INFO 8300 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 643 ms
2022-06-27 10:48:54.473 DEBUG 8300 --- [           main] com.example.demo.DemoApplication$1$1     : 3 mappings in 'requestMappingHandlerMapping'
2022-06-27 10:48:54.536  INFO 8300 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-06-27 10:48:54.543  INFO 8300 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.199 seconds (JVM running for 1.827)
2022-06-27 10:49:01.196  INFO 8300 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-06-27 10:49:01.196  INFO 8300 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2022-06-27 10:49:01.197  INFO 8300 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2022-06-27 10:49:01.210 DEBUG 8300 --- [nio-8080-exec-1] com.example.demo.DemoApplication$1$1     : Mapped to com.example.demo.DemoApplication$BooksController#readBook(String)

dependencies

org.springframework.boot:spring-boot-starter-web

application.properties

logging.level.com.example.demo=debug
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement