I'm trying to learn a few things by implementing a URL Shortner using Spring-boot, React, SQLite3.
My Controller
class is supposed to
index
when someone goes to http://localhost:8080
. Where they get the option to input a long URL, click a button, to get a shortened URL say aBcD
. This is index()
at path /
.http://localhost:8080/aBcD
. This is redirectTinyurl()
at path /{shortUrl}
.Controller
. This is createTinyurl()
at path /tinyurl
. Problem (I think) is when index
is rendered, and browser tries to fetch http://localhost:8080/main.css
it matches up with URL pattern ( /{shortUrl}
) and Spring calls redirectTinyurl()
in teh Controller
.
Is there a way to make Spring serve such resources by itself and also call my redirectTinyurl()
when appropriate? Or a way where in I can pass some calls to Spring, if I can figure out that this is a request for resource not someone trying to resolve a URL.
Interestingly it can differentiate between /tinyurl
and /{shortUrl}
.
My main.css
is already in src/main/resources/static/main.css
. Here is full folder structure.
Controller
@Controller
public class HomeController {
private final TinyUrlRepository repository;
@GetMapping(value = "/")
public String index() {
logger.info("rendering index");
return "index";
}
@GetMapping(value = "/{shortUrl}")
public ResponseEntity<String> redirectTinyurl(@PathVariable String shortUrl) {
logger.info("redirectTinyurl() shortUrl: {}", shortUrl);
UrlMapping resolvedUrl = repository.findById(Long.parseLong(shortUrl)).get();
logger.info("redirecting to: resolvedUrl: {}", resolvedUrl);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(resolvedUrl.getLongUrl()));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);
}
@PostMapping(path = "/tinyurl")
@ResponseBody
public String createTinyurl(@RequestBody UrlMappingPojo body /*contains long URL*/) {
// ... create a mapping, store in DB and return a shortened URL
}
}
index.html
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head lang="en">
<meta charset="UTF-8"/>
<title>TinyURL experiment</title>
<link rel="stylesheet" href="/main.css" />
</head>
<body>
<div id="react"></div>
<script src="built/bundle.js"></script>
</body>
</html>
Output
2021-10-02 15:01:44.656 INFO 40980 --- rendering index
2021-10-02 15:01:44.985 INFO 40980 --- redirectTinyurl() shortUrl: main.css
2021-10-02 15:01:45.011 ERROR 40980 --- Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NumberFormatException: For input string: "main.css"] with root cause
java.lang.NumberFormatException: For input string: "main.css"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68) ~[na:na]
at java.base/java.lang.Long.parseLong(Long.java:707) ~[na:na]
at java.base/java.lang.Long.parseLong(Long.java:832) ~[na:na]
at com.greglturnquist.payroll.HomeController.redirectTinyurl(HomeController.java:61) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.30.jar:9.0.30]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
.........
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
According to your code it seems like you are using Themeleaf in your project.
I replicated your problem with following,
@GetMapping(value = "/{shortUrl}")
public ResponseEntity<String> redirectTinyurl(@PathVariable String shortUrl, HttpServletResponse response) throws IOException {
if(shortUrl.equals("google"))
response.sendRedirect("http://www.google.com");
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
This will redirect to google if someone goes to http://localhost:8080/google
.
According to WebMvcConfigurationSupport documentation , first it will consider the annotated controller methods. /{shortUrl}
in your case. Since /main.css & /{shortUrl}
has the similar url partern, both resolved to same method. However /tinyirl
is specifically mentioned in the controller, it will not be confused with /{shortUrl}
.
If you want to use the main.css
and other in the same directory as your index, you will have to add wildcard patterns to exclude them using the controller method /{shortUrl}
.
But there is a simple solution for solve this.(I think it is also a best practice) That is moving your main.css to folder similar to /static/css/
.Refer the image below.
You can change the stylesheet reference as follows and you are good to go.
<link rel="stylesheet" href="/css/main.css" />
What happened here is now. /css/main.css patern looks for a annotated controller method with /{something}/{something}
. Since that is not there, it will go to resources and resolve the css file.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.