A topic that crops up time and time again is that of the elusive HTTP 404 in T5 apps.
When running a public website, for SEO reasons, it is desirable for in-exact urls to return a HTTP 404 rather than returning a 'best fit' page.For the same reasons, it is undesirable for malformed urls to return an HTTP 500. So in T5.3.4, I finally got round to converting all those URL requests to HTTP 404s. The approach taken is to generate a 'MangledUrlException' at poignant points and have an ExceptionHandler catch this specific exception to return a HTTP 404.The code changes I use to throw a 'MangledUrlException' are outlined below: My method of catching and identifying the MangledUrlException (as it's often wrapped deep inside a T5 exception) may not be suitable for all, so I've left that part out. Use Case 1 : Illegal Url "http://localhost/spa%20ce" This usually gives a java.lang.IllegalArgumentException: - Input string 'spa ce' is not valid; the character ' ' at position 4 is not valid. To your module add: @Advise(serviceInterface=ContextPathEncoder.class) @Traditional public static void adviseContextPathEncoder(MethodAdviceReceiver receiver, @Autobuild AdviseContextPathEncoder advice) throws SecurityException, NoSuchMethodException { Method method = ContextPathEncoder.class.getMethod("decodePath", String.class); receiver.adviseMethod(method, advice); } public class AdviseContextPathEncoder implements MethodAdvice { @Override public void advise(MethodInvocation invocation) { try { invocation.proceed(); } catch (IllegalArgumentException e) { throw new MangledUrlException(); } } } Use Case 2 : Illegal Url "http://localhost/index.wotever" This usually gives an org.apache.tapestry5.ioc.util.UnknownValueException - Component Index does not contain embedded component 'wotever'. @Advise(serviceInterface=ComponentEventRequestHandler.class) public static void adviseComponentEventRequestHandler(MethodAdviceReceiver receiver, @Autobuild AdviseComponentEventRequestHandler advice) throws SecurityException, NoSuchMethodException { Method method = ComponentEventRequestHandler.class.getMethod("handle", ComponentEventRequestParameters.class); receiver.adviseMethod(method, advice); } public class AdviseComponentEventRequestHandler implements MethodAdvice { private final String className; private final String methodName; public AdviseComponentEventRequestHandler() throws SecurityException, NoSuchMethodException { // set these early so we're notified of any API changes // PageImpl isn't a service, so we can't advise it directly! className = PageImpl.class.getName(); methodName = PageImpl.class.getMethod("getComponentElementByNestedId", String.class).getName(); } @Override public void advise(MethodInvocation invocation) { try { invocation.proceed(); } catch (UnknownValueException e) { if (componentIdIsMangled(e)) { ComponentEventRequestParameters params = (ComponentEventRequestParameters) invocation.getParameter(0); throw new MangledUrlException(params.getActivePageName(), MangledUrlMessages.componentNotFound(params.getNestedComponentId()), e); } } } private boolean componentIdIsMangled(UnknownValueException e) { for (StackTraceElement stackElement : e.getStackTrace()) if (stackElement.getClassName().equals(className) && stackElement.getMethodName().equals(methodName)) return true; return false; } } Use Case 3 : Unwanted context "http://localhost/index/unwanted" This usually returns the index page. @Contribute(ComponentClassTransformWorker2.class) @Primary public static void provideTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2> configuration) { configuration.addInstance("ContextNotRequiredWorker", NotRequiredWorker.class, "after:*"); } /** * It's important that that this is added "after:*" as we need to ensure no-one else has added onActivate() handlers. */ public class NotRequiredWorker implements ComponentClassTransformWorker2 { @Override public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model) { if (!model.isPage()) return; // FUTURE: this does not identify mixins which implement onActivate() if (model.handlesEvent(EventConstants.ACTIVATE)) return; support.addEventHandler(EventConstants.ACTIVATE, 0, "NotRequiredWorker activate event handler", new ComponentEventHandler() { @Override public void handleEvent(Component instance, ComponentEvent event) { if (event.getContext().length > 0) { String pageName = instance.getComponentResources().getPageName(); throw new MangledUrlException(pageName, MangledUrlMessages.contextNotWanted(event.getEventContext().toStrings())); } } }); } } -- Steve Eynon ------------------------------- "If at first you don't succeed, so much for skydiving!" --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org