在我的应用程序中,管理员用户可以控制项目的URL,因此我想在数据库中查找已注册的URL,并重定向到相关的控制器方法。
我想弄清楚我应该在几个场景中返回什么:
值得注意的是控制器方法已经使用了/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;
}
}
答案 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中描述的其中一个选项。
网址没问题 - 我想将网址重定向到相关的控制器。
只需在方法中执行业务逻辑并返回视图或类似内容。