6

I have a working url as this: localhost/info

@Controller
@RequestMapping("/info")
public class VersionController {

   @RequestMapping(value = "", method = RequestMethod.GET)
   public @ResponseBody
   Map get() {
      loadProperties();
      Map<String, String> m = new HashMap<String, String>();
      m.put("buildTimestamp", properties.getProperty("Application-Build-Timestamp"));
      m.put("version", properties.getProperty("Application-Version"));
      return m;
   }

}

and I would to register some other mappings at initializing of my application as this:

localhost/xxxx/info
localhost/yyyy/info
localhost/zzzz/info

All these urls will return same response as localhost/info

The xxxx, yyyy part of the application is changeable. I have to register custom mappings as

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("???").setViewName("???");
}

Bu this is only working for views.

Any idea for dynamic registration?

Cemo
  • 5,370
  • 10
  • 50
  • 82
  • Is there a reason you can't use URLs of the form `/affId/1313/info`? – parsifal Mar 19 '13 at 13:22
  • Yes because it was completely fictional. Actually the changing part is not id part. :( – Cemo Mar 19 '13 at 13:28
  • 3
    describing a situation that bears no relationship to your actual problem doesn't help you, and wastes the time of anyone who tries to help you. – parsifal Mar 19 '13 at 14:42
  • And if your current example isn't as completely fictional as your earlier example, @NilsH has given you a workable answer that should require less effort than registering a new `HandlerMapping`. – parsifal Mar 19 '13 at 14:43
  • Actually I just wanted to learn programmatically registering beans as Costi Ciudatu has resolved perfectly. Anyway, thanks for your efforts. – Cemo Mar 19 '13 at 15:35

6 Answers6

4

You can register a new HandlerMapping where you can add the handlers for your URL paths; the most convenient implementation would be SimpleUrlHandlerMapping.

If you want those handlers to be bean methods (like those annotated with @RequestMapping) you should define them as HandlerMethod wrappers so that the already registered RequestMappingHandlerAdapter will invoke them.

Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92
4

As of Spring 5.0.M2, Spring provides a functional web framework that allows you to create "controller"-like constructs programmatically.

What you would need to do in your case is create a proper RouterFunction for the URLs you need to handle, and then simply handle the request with the appropriate HandlerFunction.

Keep in mind however that these constructs are not part of Spring MVC, but part of Spring Reactive.

Check out this blog post for more details

geoand
  • 60,071
  • 24
  • 172
  • 190
3

I believe that simple example is worth more than 1000 words :) In SpringBoot it will look like...

Define your controller (example health endpoint):

public class HealthController extends AbstractController {

    @Override
    protected ModelAndView handleRequestInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) {
        return new ModelAndView(new MappingJacksonSingleView(), "health", Health.up().build());
    }

}

And create your configuration:

@Configuration
public class CustomHealthConfig {

    @Bean
    public HealthController healthController() {
        return new HealthController();
    }

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(Integer.MAX_VALUE - 2);
        mapping.setUrlMap(ImmutableMap.of("/health", healthController()));
        return mapping;
    }

}

Regarding the handler order - take a look about the reason here: Java configuration of SimpleUrlHandlerMapping (Spring boot)

The MappingJacksonSingleView is auxiliary class in order to return single json value for model and view:

public class MappingJacksonSingleView extends MappingJackson2JsonView {

    @Override
    @SuppressWarnings("unchecked")
    protected Object filterModel(Map<String, Object> model) {
        Object result = super.filterModel(model);
        if (!(result instanceof Map)) {
            return result;
        }

        Map map = (Map) result;
        if (map.size() == 1) {
            return map.values().toArray()[0];
        }
        return map;
    }

} 

Jackson single view source - this blog post: https://www.pascaldimassimo.com/2010/04/13/how-to-return-a-single-json-list-out-of-mappingjacksonjsonview/

Hope it helps!

Przemek Nowak
  • 7,173
  • 3
  • 53
  • 57
3

It is possible (now) to register request mapping by RequestMappingHandlerMapping.registerMapping() method.

Example:

@Autowired
RequestMappingHandlerMapping requestMappingHandlerMapping;

public void register(MyController myController) throws Exception {
    RequestMappingInfo mappingInfo = RequestMappingInfo.paths("xxxx/info").methods(RequestMethod.GET).build();
    Method method = myController.getClass().getMethod("info");
    requestMappingHandlerMapping.registerMapping(mappingInfo, myController, method);
}


Václav Kužel
  • 1,070
  • 13
  • 16
  • can you explain it please? – Umpa May 13 '20 at 10:00
  • Check out the documentation for the mentioned method for more details. Example I've posted just maps URL to an halndling method, nothing more. https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#registerMapping-org.springframework.web.servlet.mvc.method.RequestMappingInfo-java.lang.Object-java.lang.reflect.Method- – Václav Kužel May 14 '20 at 16:46
0

You could register your own HandlerMapping by extending the RequestMappingHandlerMapping, e.g. override the registerHandlerMethod.

-5

It's not quite clear what you're trying to achieve, but maybe you can use @PathVariable in your @RequestMapping, something like:

@RequestMapping("/affId/{id}")
public void myMethod(@PathVariable("id") String id) {}

Edit: Original example has changed it appears, but you might be able to use PathVariable anyway.

NilsH
  • 13,705
  • 4
  • 41
  • 59