如何为URL的数据库查找构建spring mvc自定义处理程序映射

时间:2015-08-19 05:48:38

标签: spring-mvc

在我的应用程序中,管理员用户可以控制项目的URL,因此我想在数据库中查找已注册的URL,并重定向到相关的控制器方法。

我想弄清楚我应该在几个场景中返回什么:

  1. 缺少网址 - 我想抛出404错误。
  2. 重定向网址 - 我想返回状态301,重定向返回响应。
  3. 网址没问题 - 我想将网址重定向到相关控制器。
  4. 值得注意的是控制器方法已经使用了/products/{productId}这样的标准requestMapping,并且解析得很好。

    在代码中,它找到了URL,如果它是产品,页面等,我可以解决。但是我不确定如何重定向到Controller方法,或者是否重定向了URL或者不存在如何分别返回错误代码301或404 ......

    有人可以帮忙吗?

    @Component
    public class SeoUrlHandlerMapping extends AbstractUrlHandlerMapping {
    
        private static Logger logger = LogManager.getLogger(SeoUrlHandlerMapping.class.getName());
    
        @Autowired
        private ProductSeoService productSeoService;
        /**
         * Looks up the handler for the url path.
         * @param urlPath the URL path
         * @param request the request.
         * @return
         * @throws Exception
         */
        @Override
        protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
    
            logger.entry("looking up handler for path: " + urlPath);
    
            // this is just a test.
            SeoUrl productUrl = productSeoService.findByURL(urlPath);
            if (productUrl instanceof ProductSeoUrl)
            {
                ProductSeoUrl productSeoUrl = (ProductSeoUrl) productUrl;
                logger.debug("Handling request to product  " + productSeoUrl.getProduct());
                request.setAttribute("id", productSeoUrl.getProduct().getId());
                return getApplicationContext().getBeansOfType(ProductWebController.class);
            }
            return null;
        }
    }
    

2 个答案:

答案 0 :(得分:2)

好的,没有答案,但我会发布我最终提出的内容。我不确定它是否是最好的解决方案,但似乎对我来说没问题。可能有更好的方法来设置模型映射,或重写路径参数,但servlet请求正常工作......

所以这是主要的MappingHandler:

/**
 * The SeoUrlHandlerMapping will map between SEO URL requests and controller method
 */
@Component
public class SeoUrlHandlerMapping extends RequestMappingHandlerMapping {

    private static Logger logger = LogManager.getLogger(SeoUrlHandlerMapping.class.getName());

    @Autowired
    private ProductSeoService productSeoService;

    private final Map<String, HandlerMethod> handlerMethods = new LinkedHashMap<String, HandlerMethod>();


    @Override
    protected void initHandlerMethods() {

       logger.debug("initialising the handler methods");
        String[] beanNames =
                getApplicationContext().getBeanNamesForType(Object.class);

        for (String beanName : beanNames) {
            Class clazz = getApplicationContext().getType(beanName);
            final Class<?> userType = ClassUtils.getUserClass(clazz);

            if (isHandler(clazz)){
                for (Method method: clazz.getMethods())
                {
                    SeoUrlMapper mapper = AnnotationUtils.getAnnotation(method, SeoUrlMapper.class);
                    if (mapper != null)
                    {
                        RequestMappingInfo mapping = getMappingForMethod(method, userType);
                        HandlerMethod handlerMethod = createHandlerMethod(beanName, method);
                        this.handlerMethods.put(mapper.seoType(), handlerMethod);
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     * Expects a handler to have a type-level @{@link org.springframework.stereotype.Controller} annotation.
     */
    @Override
    protected boolean isHandler(Class<?> beanType) {
        return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
                (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
    }

    /**
     * The lookup handler method, maps the SEOMapper method to the request URL.
     * <p>If no mapping is found, or if the URL is disabled, it will simply drop throug
     * to the standard 404 handling.</p>
     * @param urlPath the path to match.
     * @param request the http servlet request.
     * @return The HandlerMethod if one was found.
     * @throws Exception
     */
    @Override
    protected HandlerMethod lookupHandlerMethod(String urlPath, HttpServletRequest request) throws Exception {

        logger.entry("looking up handler for path: " + urlPath);

        // this is just a test.
        SeoUrl productUrl = productSeoService.findByURL(urlPath);
        if (productUrl instanceof ProductSeoUrl) {
            ProductSeoUrl productSeoUrl = (ProductSeoUrl) productUrl;

            if (productSeoUrl.getStatus().equals(SeoUrlStatus.OK) || productSeoUrl.getStatus().equals(SeoUrlStatus.DRAFT))
            {
                request.setAttribute(SeoConstants.ID, productSeoUrl.getProduct().getId());
                request.setAttribute(SeoConstants.URL_STATUS, productSeoUrl.getStatus().toString());
                return this.handlerMethods.get("PRODUCT");
            }else if (productSeoUrl.getStatus().equals(SeoUrlStatus.REDIRECTED))
            {
                request.setAttribute(SeoConstants.REDIRECT_URL, productSeoUrl.getRedirectURL());
                return this.handlerMethods.get("REDIRECT");
            }

            // otherwise we let it return 404 by dropping through.
        }

        return null;
    }
}

然后我在Controller方法上使用自定义注释来隔离处理程序方法:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SeoUrlMapper {
        /**
         * Assigns a type to this mapping.
         * <p><b>This should match the SEOEntityType constants</b></p>.
         */
        String seoType();
}

最后在我的Controller方法中,我设置了注释来指示方法:

@SeoUrlMapper(seoType = "REDIRECT")
public RedirectView issueRedirect(ModelMap map, HttpServletRequest request)
{
    logger.entry();
    RedirectView view = new RedirectView();
    view.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
    view.setUrl((String)request.getAttribute("REDIRECT_URL"));
    view.setExposeModelAttributes(false);
    logger.exit();
    return view;
}

@SeoUrlMapper(seoType = "PRODUCT")
public String viewProductInternal(ModelMap map, HttpServletRequest request)
{
    Long id = (Long) request.getAttribute(SeoConstants.ID);
    Product product = productService.findForDetailView(id);
    return commonViewProduct(product, map);
}   

答案 1 :(得分:1)

我不确定你是否真的需要HandlerMapping。 Controller无法处理所有这些情况吗?

  

缺少网址 - 我想抛出404错误。

我认为最好的方法是编写自己的异常(例如MissingUrlException),如果适用则抛出此异常并编写自己的错误处理程序,例如

@ExceptionHandler(MissingUrlException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public String handleMissingUrl(MissingUrlException ex) {
    // return a view or anything you like
}
  

重定向网址 - 我想返回状态301,重定向回到响应。

您可以在此处执行相同操作,但设置301(即HttpStatus.MOVED_PERMANENTLY)并使用A Guide To Spring Redirects中描述的其中一个选项。

  

网址没问题 - 我想将网址重定向到相关的控制器。

只需在方法中执行业务逻辑并返回视图或类似内容。