Legacy Site Redirects Filter
If your legacy URLs patterns are too complex for a Vanity Redirect, it may be simpler to handle these redirects in a Filter.
The logic to process these legacy URLs redirects will be different for every project, but here's an example to get you started.
This one handles two different use cases, which is not at all uncommon when migrating from a legacy site.
note
This code is an example only and will need customization by a developer before it is useful in a project.
1import java.util.List;2import java.util.regex.Matcher;3import java.util.regex.Pattern;4import javax.servlet.Filter;5import javax.servlet.FilterChain;6import javax.servlet.http.HttpServletRequest;7import javax.servlet.http.HttpServletResponse;89import com.psddev.cms.db.Content;10import com.psddev.cms.db.PageFilter;11import com.psddev.dari.db.Predicate;12import com.psddev.dari.db.PredicateParser;13import com.psddev.dari.db.Query;14import com.psddev.dari.util.AbstractFilter;1516/**17* Redirect Legacy URLs to the appropriate permalink.18*/19public class LegacyUrlRedirectFilter extends AbstractFilter implements AbstractFilter.Auto {2021// Legacy URL was /story/normalized-headline/12345622// "/story" is the prefix23// "normalized-headline" can be ignored24// "123456" has been saved on every migrated object using the field "migration.legacyId" with a prefix of "story:"25private static final Pattern LEGACY_STORY_URI_PATTERN = Pattern.compile("^/story/[^/]+/(.*)$");26private static final String STORY_ID_PREFIX = "story:";2728// Legacy section path was /section.php?id=78929// "789" has been saved on every migrated object using the field "migration.legacyId" with a prefix of "section:"30private static final String LEGACY_SECTION_PATH = "/section.php";31private static final String LEGACY_SECTION_ID_PARAMETER = "id";32private static final String SECTION_ID_PREFIX = "section:";3334private static final String LEGACY_ID_FIELD = "migration.legacyId";3536// Helper method to build legacy story ID predicate37private static Predicate legacyStoryIdPredicate(String storyId) {38return PredicateParser.Static.parse(LEGACY_ID_FIELD + " = ?", STORY_ID_PREFIX + storyId);39}4041// Helper method to build legacy section ID predicate42private static Predicate legacySectionIdPredicate(String sectionId) {43return PredicateParser.Static.parse(LEGACY_ID_FIELD + " = ?", SECTION_ID_PREFIX + sectionId);44}4546// Helper method to redirect47private static void sendRedirect(HttpServletResponse response, String location) {48response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);49response.setHeader("Location", location);50}5152// This should run *before* PageFilter.53@Override54public void updateDependencies(Class<? extends AbstractFilter> filterClass, List<Class<? extends Filter>> dependencies) {55if (PageFilter.class == filterClass) {56dependencies.add(LegacyUrlRedirectFilter.class);57}58}5960@Override61protected void doRequest(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws Exception {6263Object mainObject = PageFilter.Static.getMainObject(request);6465// Only run if a mainObject cannot be found66if (mainObject == null) {67String requestPath = request.getRequestURI();6869// Attempt to match the legacy section path70if (LEGACY_SECTION_PATH.equals(requestPath)) {71String legacySectionId = request.getParameter(LEGACY_SECTION_ID_PARAMETER);72if (legacySectionId != null && !legacySectionId.isEmpty()) {73Content content = Query.from(Content.class)74.where(legacySectionIdPredicate(legacySectionId))75.first();76if (content != null) {77sendRedirect(response, content.getPermalink());78return;79}80}81}8283// Attempt to match the legacy story path and extract the story ID from the path84Matcher storyPathMatcher = LEGACY_STORY_URI_PATTERN.matcher(requestPath);85if (storyPathMatcher.matches()) {86String legacyStoryId = storyPathMatcher.group(1);87if (legacyStoryId != null && !legacyStoryId.isEmpty()) {88Content content = Query.from(Content.class)89.where(legacyStoryIdPredicate(legacyStoryId))90.first();91if (content != null) {92sendRedirect(response, content.getPermalink());93return;94}95}96}97}9899// Otherwise, continue processing the request.100chain.doFilter(request, response);101}102}