Spring Boot URL Mapping - java

I was trying to research this specific problem online but I don't really know what to search up on Google.
My question is: is there a way to auto-redirect a url to another url. For example: I type
http://localhost:8080 which directs you to http://localhost:8080/
I was wondering if I could make it so http://localhost:8080 redirect to http://localhost:8080/home

Use the following status HttpStatus.PERMANENT_REDIRECT to indicate that this URL(/) is permanently moved and use Location header to indicate what is the new location(/home).
public class GreetingController {
#GetMapping("/")
public ResponseEntity<Map<String, String>> sayHello1() {
return ResponseEntity.status(HttpStatus.PERMANENT_REDIRECT)
.header("Location", "/home").build();
}
#GetMapping("/home")
public ResponseEntity<Map<String, String>> sayHello() {
return ResponseEntity.ok()
.body(Collections.singletonMap("message", "Hello world!"));
}
}
Hope this will solve your problem.

this is works for me.
#RestController
#RequestMapping("")
public class TestController {
#GetMapping(value = "")
public ModelAndView redirect(HttpServletRequest request) {
String redirectUrl = request.getRequestURL().toString() + "home";
return new ModelAndView("redirect:" + redirectUrl);
}
}

public class TestController {
#GetMapping(value = "/")
public String home() {
return "redirect:/home";
}
}
You should not put #ResponseBody annotation in method definition else the redirect will not work.

Related

Spring boot: how to match all routes?

I'm developing a simple web application on Spring boot 1.5.3 and I need all the routes to send static index.html file. Now, I have this:
#Controller
public class IndexController {
#RequestMapping("/*")
public String index(final HttpServletRequest request) {
final String url = request.getRequestURI();
if (url.startsWith("/static")) {
return String.format("forward:/%s", url);
}
return "forward:/static/index.html";
}
}
My application contains only static assets and REST API. But the problem is that the controller shown above only matches the first-level url like /index, /department etc. I want to match all url levels like /index/some/other/staff etc. How can I do that?
PS. I've tried to use the template /** in #RequestMapping, but my application has broken with the StackOverflow error.
UPDATE
If add one more level to url, then all will work as expected:
#Controller
public class IndexController {
#RequestMapping("/test/**")
public String index(final HttpServletRequest request) {
final String url = request.getRequestURI();
if (url.startsWith("/static")) {
return String.format("forward:/%s", url);
}
return "forward:/static/index.html";
}
}
All requests to /test, /test/some/other/staff will return index.html, but I need to start with /.
Above answer didn't really work for me. As per official Spring doc it should be done like this:
#RequestMapping(value = "{*path}", method = RequestMethod.GET)
#ResponseBody
public String handleAll(#PathVariable(value = "path") String path) {
log.debug("Requested path is: {}", path);
return "forward:/static/index.html";
}
You can try this:
#Controller
public class IndexController {
#RequestMapping(value = "/**/{path:[^\\.]*}")
public String index(final HttpServletRequest request) {
final String url = request.getRequestURI();
if (url.startsWith("/static")) {
return String.format("forward:/%s", url);
}
return "forward:/static/index.html";
}
}

Can Spring boot dynamically create end points based on the content of the property file?

So far I am creating end points like this:
#RequestMapping(value = "/test", method = RequestMethod.POST)
public #ResponseBody String indexPost(HttpServletRequest request, HttpServletResponse response)
throws Exception {
//Doing calculations
return "Result";
}
But I would like to reach the application.properties when the server starts, to read out the data stored like this:
methods: {
"endpointOne": "DBStoredProcedure1",
"endpointTwo": "DBStoredProcedure2"
}
So when my Spring Boot application starts, it would create all the POST endpoints based on the property file with the names of the first parameters (like "endpointOne"), and would call (and return the result of) the stored procedure which is defined as the second parameter (like "DBStoredProcedure1").
Is it possible to do?
Yes you can. A little bit differently though than you try to do it at the moment.
The best is to use a "PathVariable" - you find detailed information here:
https://spring.io/guides/tutorials/bookmarks/
http://javabeat.net/spring-mvc-requestparam-pathvariable/
Your method at the Controller class would look something like this:
#RequestMapping(value="/{endPoint}", method=RequestMethod.POST)
public String endPoint(#PathVariable String endPoint) {
//Your endPoint is now the one what the user would like to reach
//So you check if your property file contains this String - better to cache it's content
//If it does, you call the service with the regarding Stored Procedure.
String sPName = prop.getSPName(endPoint); //You need to implement it.
String answer = yourService.execute(sPName);
return answer;
}
Obviously you need to implement a method to handle those queries which are not found in the property file, but you get the idea.
You can use a wild card "/*" as the value in controller. So that all your endpoints would hit the same controller request method.
Below is the code sample.
#RequestMapping(value = "/*", method = RequestMethod.GET, headers="Accept=*/*", produces = { "application/json" })
public ResponseEntity<Object> getData(#RequestParam Map<String, String> reqParam, HttpServletRequest request)
throws WriteException, IOException{
MessageLogger.infoLog(EquityControllerImpl.class, GETQADTRSTOCKPRICELOGS,
ENTRY);
// Get Request URI
MessageLogger.infoLog("Request URI: " + request.getRequestURI());
String requestUri = request.getRequestURI();
//Read all request parameters
Map<String, String> requestParamMap = new HashMap<>();
for (Map.Entry<String, String> param: reqParam.entrySet()
) {
System.out.println("Parameter: " + param.getKey() + " ----> Value: " + param.getValue());
requestParamMap.put(param.getKey(),param.getValue());
}
}
Also you can define static swagger.json and use this in the swagger configuration.
#Configuration
#EnableSwagger2
#Import(SpringDataRestConfiguration.class)
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.finance.dataplatform.*"))
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build().apiInfo(getApiInfo());
}
private ApiInfo getApiInfo() {
return new ApiInfoBuilder().title("Investment Management").build();
}
private static Predicate<String> matchPathRegex(final String... pathRegexs) {
return new Predicate<String>() {
#Override
public boolean apply(String input) {
for (String pathRegex : pathRegexs) {
if (input.matches(pathRegex)) {
return true;
}
}
return false;
}
};
}
#Bean
WebMvcConfigurer configurer () {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler("/config/swagger.json")
.addResourceLocations("classpath:/config");
}
};
}
#Primary
#Bean
public SwaggerResourcesProvider swaggerResourcesProvider(InMemorySwaggerResourcesProvider defaultResourcesProvider) {
return () -> {
SwaggerResource wsResource = new SwaggerResource();
wsResource.setName("default");
wsResource.setSwaggerVersion("2.0");
wsResource.setLocation("/config/swagger.json");
//List<SwaggerResource> resources = new ArrayList<>(defaultResourcesProvider.get());
List<SwaggerResource> resources = new ArrayList<>();
resources.add(wsResource);
return resources;
};
}
}

How to access a PathVariable of a controller specified at class level in Spring?

Can I do something like this with Spring MVC ?
#RequestMapping(value = "/{root}")
public abstract class MyBaseController {
#PathVariable(value = "root")
protected ThreadLocal<String> root;
}
#Controller
public class MyController extends MyBaseController {
#RequestMapping(value = "/sayHello")
#ResponseBody
public String hello() {
return "Hello to " + this.root.get();
}
}
When I request to http://..../roberto/sayHello, I get this as response:
Hello to roberto
You can have a path variable in the controller URL-prefix template like this:
#RestController
#RequestMapping("/stackoverflow/questions/{id}/actions")
public class StackOverflowController {
#GetMapping("print-id")
public String printId(#PathVariable String id) {
return id;
}
}
so that when a HTTP client issues a request like this
GET /stackoverflow/questions/q123456/actions/print-id HTTP/1.1
the {id} placeholder is resolved as q123456.
you can code like this:
#RequestMapping("/home/{root}/")
public class MyController{
#RequestMapping("hello")
public String sayHello(#PathVariable(value = "root") String root, HttpServletResponse resp) throws IOException {
String msg= "Hello to " + root;
resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
out.println(msg);
out.flush();
out.close();
return null;
}
}
and the result like this:
and,you can use ModelAndView return msg value to the jsp or other html page.
According to the docs:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/PathVariable.html
the PathVariable annotation is itself annotated with #Target(value=PARAMETER) so it shouldn't be possible to be used the way you're saying as it's only applicable to method parameters.

How to set default handler method in controller in Spring mvc?

I am working on a Spring mvc application in which I have to display a list of locations. I have a controller method for location. Following is my controller method code:
#RequestMapping("/location")
public class LocationController {
#RequestMapping(value = "/home")
public String showAllLocations(ModelMap modelMap) {
logger.info("showAllLocations() begins:");
try {
List<LocationModel> locationList = locationService
.getAllLocations("");
modelMap.addAttribute("locationlist", locationList);
} catch (Exception e) {
logger.debug("Error while getting locations: " + e.getMessage());
e.printStackTrace();
}
return "LocationHome";
}
}
It works fine when I user following URL:
http://www.example.com:8080/myapp/location/home
But when I use http://www.example.com:8080/myapp/location, it shows error.
How can I view location list without using 'home', by following URL:
http://www.example.com:8080/myapp/location
#RequestMapping(value = "/home")
public String doHome(ModelMap modelMap) {
...
}
#RequestMapping(value = "/**")
public String doDefault(ModelMap modelMap) {
...
}
Make sure you put more specific request handler before less specific one.
#RequestMapping(value = "/", method = { RequestMethod.GET, RequestMethod.POST })
This can map you to a default handler.
You have the request mapping for the LocationController here ,
#RequestMapping("/location")
public class LocationController {..}
So all the URL to be intercepted by this controller should have the pattern /location followed by the method request mapping as here ,
#RequestMapping(value = "/home")
public String showAllLocations(ModelMap modelMap) {..}
If you need to intercept the request for http://www.example.com:8080/myapp/location . Just remove the url mapping from the controller and assign it to the method.

Redirect to an external URL from controller action in Spring MVC

I have noticed the following code is redirecting the User to a URL inside the project,
#RequestMapping(method = RequestMethod.POST)
public String processForm(HttpServletRequest request, LoginForm loginForm,
BindingResult result, ModelMap model)
{
String redirectUrl = "yahoo.com";
return "redirect:" + redirectUrl;
}
whereas, the following is redirecting properly as intended, but requires http:// or https://
#RequestMapping(method = RequestMethod.POST)
public String processForm(HttpServletRequest request, LoginForm loginForm,
BindingResult result, ModelMap model)
{
String redirectUrl = "http://www.yahoo.com";
return "redirect:" + redirectUrl;
}
I want the redirect to always redirect to the URL specified, whether it has a valid protocol in it or not and do not want to redirect to a view. How can I do that?
Thanks,
You can do it with two ways.
First:
#RequestMapping(value = "/redirect", method = RequestMethod.GET)
public void method(HttpServletResponse httpServletResponse) {
httpServletResponse.setHeader("Location", projectUrl);
httpServletResponse.setStatus(302);
}
Second:
#RequestMapping(value = "/redirect", method = RequestMethod.GET)
public ModelAndView method() {
return new ModelAndView("redirect:" + projectUrl);
}
You can use the RedirectView. Copied from the JavaDoc:
View that redirects to an absolute, context relative, or current request relative URL
Example:
#RequestMapping("/to-be-redirected")
public RedirectView localRedirect() {
RedirectView redirectView = new RedirectView();
redirectView.setUrl("http://www.yahoo.com");
return redirectView;
}
You can also use a ResponseEntity, e.g.
#RequestMapping("/to-be-redirected")
public ResponseEntity<Object> redirectToExternalUrl() throws URISyntaxException {
URI yahoo = new URI("http://www.yahoo.com");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(yahoo);
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
}
And of course, return redirect:http://www.yahoo.com as mentioned by others.
You can do this in pretty concise way using ResponseEntity like this:
#GetMapping
ResponseEntity<Void> redirect() {
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create("http://www.yahoo.com"))
.build();
}
Looking into the actual implementation of UrlBasedViewResolver and RedirectView the redirect will always be contextRelative if your redirect target starts with /. So also sending a //yahoo.com/path/to/resource wouldn't help to get a protocol relative redirect.
So to achieve what you are trying you could do something like:
#RequestMapping(method = RequestMethod.POST)
public String processForm(HttpServletRequest request, LoginForm loginForm,
BindingResult result, ModelMap model)
{
String redirectUrl = request.getScheme() + "://www.yahoo.com";
return "redirect:" + redirectUrl;
}
Another way to do it is just to use the sendRedirect method:
#RequestMapping(
value = "/",
method = RequestMethod.GET)
public void redirectToTwitter(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.sendRedirect("https://twitter.com");
}
For me works fine:
#RequestMapping (value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<Object> redirectToExternalUrl() throws URISyntaxException {
URI uri = new URI("http://www.google.com");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(uri);
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
}
For external url you have to use "http://www.yahoo.com" as the redirect url.
This is explained in the redirect: prefix of Spring reference documentation.
redirect:/myapp/some/resource
will redirect relative to the current Servlet context, while a name such as
redirect:http://myhost.com/some/arbitrary/path
will redirect to an absolute URL
Did you try RedirectView where you can provide the contextRelative parameter?
This works for me, and solved "Response to preflight request doesn't pass access control check ..." issue.
Controller
RedirectView doRedirect(HttpServletRequest request){
String orgUrl = request.getRequestURL()
String redirectUrl = orgUrl.replaceAll(".*/test/","http://xxxx.com/test/")
RedirectView redirectView = new RedirectView()
redirectView.setUrl(redirectUrl)
redirectView.setStatusCode(HttpStatus.TEMPORARY_REDIRECT)
return redirectView
}
and enable securty
#EnableWebSecurity
class SecurityConfigurer extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
}
}
In short "redirect://yahoo.com" will lend you to yahoo.com.
where as "redirect:yahoo.com" will lend you your-context/yahoo.com ie for ex- localhost:8080/yahoo.com

Categories