SEO friend urls for spring mvc application - spring

I have an application with urls like site.com/article/1/title.htm
I have #RequestMapping /article/{id}/{title}.htm that server this request and gets the article.
What I am looking achieve is to have a url like site.com/title.htm but cant think of a way to do that using Spring MVC because I need id of the article. any ideas? Thanks in advance

When you create an article, you need to create the SEO-friendly URL also and persist it, along with the article. Now you need to have a repository method that allows you to retrieve articles by permalink, and a Spring MVC endpoint that calls that repository method.
Using the title may not be a good idea, as the title is usually not URL-friendly, and may eventually be non-unique. But it is a good idea to use the title as the input for the permalink.
Here's a sample permalink algorithm:
Take the title
replace all occurrences of one or more space or punctuation with a single dash
replace all non-ascii characters with their ascii neighbors
check whether that permalink already exists
if it does, add a counter
This is how the read path could look like:
#Autowired
private ArticleRepository ar;
#RequestMapping(value="/article/{id}/{ignored}") #ResponseBody
public Article getByIdAndIgnorePermalink(#PathVariable String id, #PathVariable String ignored){
return ar.getById(id);
}
#RequestMapping(value="/article/{title}.html") #ResponseBody
public Article getByPermalink(#PathVariable String permalink){
return ar.getByPermalink(permalink);
}

There is no way send hidden id obviously, so it has to be done through a permalink of the article or simply via title, to achieve site.com/title.html you need to get rid of all the fixed bits by adding this request mapping rule:
#RequestMapping(value = "/**/{articleTitle}.html"
but to get the article you can obviously use id as its not there in the URL and have to work with that articleTitle or generate a permalink as suggested by #Sean above.

Related

Influencing order of RequestMapping processing

In short this is what I'm searching for: I want to create a RequestMapping that catches all URLs except a small list I don't want to catch.
The minimum to exclude is everything below /webjars/, I also would like to exclude other URLS like /actuator/ and probably more.
Background information
We need to replace an old CMS which has literally thousands of different URLs. All URLs need to be detected and checked against a database and then the users shall be presented with a landing page, this landing page will then redirect the user to the new CMS target URL.
The logic that needs to be applied is too complicated for some Apache / nginx magic, therefore I wrote a Spring Boot application that can accomplish this.
I've created a #RequestMapping(value = "**", method = RequestMethod.GET) that catches all GET requests (these are the one I want to grab and react on) and a #RequestMapping(value = "**") for the other http verbs like POST and DELETE which I simply abort by sending a http error status code.
This works fine and the (simplified) code looks like this:
#RequestMapping(value = "**", method = RequestMethod.GET)
public String catchAccepted(HttpServletRequest request, HttpServletResponse httpServletResponse, Model model) {
model.addAttribute("targetUrl", ua.deriveNewUrl(request));
return "redirect";
}
#RequestMapping(value = "**")
#ResponseBody
public ResponseEntity<Object> catchDenied(HttpServletRequest request, HttpServletResponse httpServletResponse) {
return new ResponseEntity<Object>(HttpStatus.I_AM_A_TEAPOT);
}
The page that gets displayed for all the GET requests is based on a Thymeleaf template which uses Bootstrap in order to do the layout job.
In order to include Bootstrap I use the webjars-locator and org.webjars.bootstrap, the resources are included by specifying <script src="/webjars/bootstrap/js/bootstrap.min.js"></script> in the redirect.html Thymeleaf template.
Problem
The problem is, that my ** mapping on GET also gets applied to the /webjars/... call and instead of the desired js code I get whatever my redirect template returns.
I found no way to tell Spring Boot about the desired order.
First I would like to have Spring Boot handle the webjars mapping, then my other mapping.
Attempts so far
I checked other posts on SO but they only work when I have access to the sourcecode where the mapping is made. And I don't have access to the webjars locator / see no point in changing it just to solve this issue.
I also tried to set up a "anything that is not related to webjars" mapping like this:
#RequestMapping(value = "^(?!webjars$|actuator$).*", method = RequestMethod.GET)
But this doesn't have the desired effect because the RequestMapping only seems to support ant-stlye paths, which doesn't support negations because Ant (in contrast to Spring Boot) has a field for includes and excludes: https://ant.apache.org/manual/dirtasks.html
Negating the mapping seems only to be possible for params, not for the path: Change #RequestMapping order
I didn't yet find a way to influence the order if other mappings come from code I can not incluence.
But I found a way to configure "catch all except of ...":
#RequestMapping(value = { "", "/", "{url:(?!webjars$|actuator$).*}/**" }, method = RequestMethod.GET)
This configures three mappings. The first two are there to handle calls to the root of the webserver. The third configures a path pariable which I could also put into a #PathVariable but in my scenario the value doesn't matter. If you configure a path variable you need to give it a default because the pattern will only be satisfied depending on the value of your URL.
The regex tells Spring Boot only to react if the url doesn't contain webjars or actuator. The regex itself is best explained by using regex101:

Spring RESTful Service URL to object

How can I convert a URL (http://127.0.0.1:8080/authors/1) to domain object?
I will let my clients submit URLs as foreign keys and I have to resolve them somehow.
For example I want to create a book with the author 1
POST /books
{"title":"Harry Potter", "author":"http://127.0.0.1:8080/authors/1"
I found a class named UriToEntityConverter that sounds right but of course 0 tutorials or examples.
I am serving my objects from a #RestController.
you could simply define your path using request mapping and store the variable using pathVariable annotation
#RequestMapping(value="/authors/{id}", method=RequestMehtod.POST)
public RequestEntity<String> doSomething(#PathVariable("id") long id) //id will contain your id

Dynamic URL structure with Spring 4

We have a Spring MVC webapp and our product URL structure needs to be SEO friendly. Such as www.mydomain.com/{productname}. Every time we add a product to inventory, we want to have URL to display those product details.
We could not figure our how to generate dynamic URLs with Spring Controller Resource path.
Can someone please help us.
Would appreciate your help.
Thanks
Raj
A Spring URL structure like www.mydomain.com/{productname} would seem to imply that 'productname' is always unique. If so, each product could have a property called 'name' or 'productname'; this property could be used to retrieve the relevant product in your controller method. This will only work if every 'productname' is unique.
What you are looking for is URI Template Patterns. In Spring MVC, #PathVariable annotation is used to bind an argument to the value of URI template variable.
#RequestMapping(path="/{productname}", method=RequestMethod.GET)
public String findProduct(#PathVariable String productname, Model model) {
List<Product> product = productService.findProductsByName(productname);
model.addAttribute("product", product);
return "displayProduct";
}
Note that the service call returns List<Product> instead of one Product, since ideally, product name is not unique to one item. If you want the URI to identify exactly one product, make sure that product name is unique to one product only.

Spring passing object/string between controllers (GET & POST)

I've been struggling with passing some value between controller.
I have one controller like this:
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String addGet(HttpServletRequest request, #ModelAttribute(value="branch") Branch branch, Model model, blahblahblah)
//What I want to pass and re use:
String loadRespond;
try{
loadRespond= *SOME LOAD STRING METHOD*;
branch= branchManager.convertString(loadRespond); //METHOD TO SPLIT STRING & INDUCT TO OBJECT
}catch{exception){
//blabla
}
After I successfully inducted all the attributes into the object branch,i show them all through a binding form. What i want to do is, when i'm going to update the data/change some attribute, i want to compare the old branch to the new changed branch. This means that i have to pass the old branch object or the loadRespond string onto the POST method so that can be used. Do anyone have any idea of how to do this? Maybe to assign it to hidden type field in the jsp? and then use it on the controller with request mapping /add of method type post? Thanks..I'm a newbie..
Why don't you try out with session scope ?
store your old branch into the session . and when you get the new object compare with the old one (by retrieving from session)
You can save into session as any of both,
request.getSession().setAttribute("sessionvar", "session value");
#SessionAttributes("sessionvar")
A nice Example here to start with it.
Side-note : your question title doesnt quite expalain your problem and the solutions may vary
As San Krish notes in his answer the most common way is to use #SessionAttributes and pass objects/data using them.
This is useful if you don't worry about user moving backwards and forwards in a page, or want basic control of the object.
Now if you want to have a chain where controller 1 passes to controller 2 which may pass to controller 3 your best bet is to implement web flows.
Summary:
For short and sweet and quick: SessionAttributes is the way to go, example here http://www.intertech.com/Blog/understanding-spring-mvc-model-and-session-attributes/
For chain passing, greater control and validation use Spring Web Flows.

Passing multiple values as query string is good or bad practice in MVC

In my page I ve to pass multiple values to controller.
My URL looks something like :
http://blah/Search/Page/?type=new&keywords=blahblah&sortType=Date
Is Passing multiple values as query string good practice in MVC ? or We can have slash separated URL, by using introducing custom routing?
NB: Lets consider all the values in my query string, are not secure / sensitive data.
I wouldn't consider it a bad practice, as long as it's not senstive data. It just depends if you want to write a custom route in your global.asax to handle it or not. The custom routes provide a cleaner url forsure. Also, for more savy users if they understand the concept on your site, it's more intuitive.
So consider this:
http://baseballcards/topps/1980 // search for baseball cards made by topps in the 1980s
http://recipes/deserts/pies // search for a desert recipe that are pies
http://code/csharpe/linq // search for csharp code that has linq examples
In these examples we can almost read the url like a sentence, making it more intuitive, giving a user the ability to plug and play. It clearly denotes the query almost like a breadcrumb, indicating exactly what the context will be. I personally like this. But either way is a good approach.
To extend with more parameters:
routes.MapRoute(
"SearchRecipes",
"Search/Recipes/{category}/{type}",
new { controller = "Search", action = "Recipes", category = "all" , type = ""}
);
Some examples:
Search/Recipes/Deserts/Pie
Search/Recipes/Dinner/Beef
Search/Recipes/Lunch/Salads
Select later (query string in route values) in case,
If you are concerned about header length.( By default get parameters are part of headers, and web server accept 1024 byte header length by default in IIS7).
Hide logical implementation of your code.
Url looks good and easier to remember.
Otherwise Both the approaches work equally.
I think passing search parameters in the query string is the way you should go, especially if all your parameters are optional. It also enables you to use normal method="get" forms without hassle.
I don't think "security/personal data" has anything to do with this since the query string is a part of the URL just like the path is.
IMO I think this is absolutely fine. I think it is actually preferable to use querystrings in MVC when the path represents the function and the querystring parameters represent the filters, e.g. as in a search.
However, I wouldn't use querystring parameters for data that represent information about the content retrieved. So I would include the year and month of an article in the path but not the page number if returning more than one page of articles for the year and month.

Resources