我如何让HandlerMethod匹配过滤器中的HttpServletRequest

时间:2017-08-09 15:43:00

标签: spring-boot

我正在编写一个简单的代理应用程序,并希望映射的网址将由我的控制器处理,但其他网址(包括错误)可以转发到另一个不同的地址。所以我使用Filter而不是HandlerInterceptorAdapter如果找不到resourece则无法调用,因为某些" resourece路径处理程序"处理它。

期望

http://localhost:8090/upload.html> Filter> http://localhost:8092/upload.html
http://localhost:8090/files/upload> Controller> http://localhost:8092/files/upload

不会

http://localhost:8090/upload.html> Filter> http://localhost:8092/upload.html
http://localhost:8090/files/upload> Controller> http://localhost:8092/files/upload

或者

http://localhost:8090/upload.html> Interceptor> http://localhost:8090/error未找到 http://localhost:8090/files/upload> Filter> http://localhost:8092/files/upload

演示

我在Filter的子类中设置了WebMvcConfigurerAdapter

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    private javax.servlet.Filter proxyFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
                System.out.println("[doFilterInternal]isCommitted=" + response.isCommitted() + ", URI = " + request.getRequestURI());
                // if(!isRequestMappedInController(request, "my.pakcage"))
                httpProxyForward(request, response);
            }
        };
    }

//    @Bean
//    private FilterRegistrationBean loggingFilterRegistration() {
//        FilterRegistrationBean registration = new FilterRegistrationBean(proxyFilter());
//        registration.addUrlPatterns("/**");
//        return registration;
//    }

    Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                // How I determine a controller has handled the request in my interceptor?
                if (handler instanceof HandlerMethod) {
                    HandlerMethod handlerMethod = ((HandlerMethod) handler);
                    if (handlerMethod.getMethod().getDeclaringClass().getName().startsWith("nxtcasb.casbproxy")) {
                        System.out.println("[preHandle]dealt: request uri = " + request.getRequestURI() + ", HandlerMethod = " + ((HandlerMethod) handler).getMethod());
                        return true;
                    } else {
                        System.out.println("[preHandle]isCommitted=" + response.isCommitted() + ", HandlerMethod = " + ((HandlerMethod) handler).getMethod());
                    }
                }
                // act as an api-gateway
                System.out.println("[preHandle]undealt: request uri = " + request.getRequestURI() + ", handler = " + handler);

                //ModelAndView modelView = new ModelAndView("redirect: http://www.bing.com");
                //throw new ModelAndViewDefiningException(modelView);
                httpProxyForward(request, response);
                return false;
            }

            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                if (handler instanceof HandlerMethod) {
                    System.out.println("[postHandle]dealt: uri = " + request.getRequestURI() + ", handler = " + ((HandlerMethod) handler).getMethod());
                } else {
                    System.out.println("[postHandle]undealt uri = " + request.getRequestURI() + ", handler = " + handler);
                }
            }
        }).addPathPatterns("/**", "/error");
    }

    /**
     * this is the same as  <mvc:default-servlet-handler/> <!-- This tag allows for mapping the DispatcherServlet to "/" -->
     *
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //registry.addResourceHandler("/**").addResourceLocations("classpath:/public");
    }

    protected void httpProxyForward(HttpServletRequest request, HttpServletResponse response) {
        HttpClient httpClient = CreateHttpClient();
        HttpUriRequest targetRequest = null;
        HttpResponse targetResponse = null;
        try {
            targetRequest = createHttpUriRequest(request);
            targetResponse = httpClient.execute(targetRequest);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // make sure the entire entity was consumed, so the connection is released
            if (targetResponse != null) {
                EntityUtils.consumeQuietly(targetResponse.getEntity()); // @since 4.2
                //Note: Don't need to close servlet outputStream:
                // http://stackoverflow.com/questions/1159168/should-one-call-close-on-httpservletresponse-getoutputstream-getwriter
            }
        }
    }
}

api url /files/upload

@RestController
@RequestMapping(value = "/files")
public class FileUploadProxyController {

    private static final Logger logger = LoggerFactory.getLogger(FileUploadProxyController.class);

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public ResponseEntity upload(HttpServletResponse response, HttpServletRequest request) {
        try {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            Iterator<String> it = multipartRequest.getFileNames();
            MultipartFile multipart = multipartRequest.getFile(it.next());
            String fileName = multipart.getOriginalFilename();

            File dir = new File("files", "proxy-uploaded");
            dir.mkdirs();

            logger.debug("current dir = {}, uploaded dir = {}", System.getProperty("user.dir"), dir.getAbsolutePath());

            File file = new File(dir, fileName);
            Files.copy(multipart.getInputStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
            //FileCopyUtils.copy(multipart.getInputStream())

//            byte[] bytes = multipart.getBytes();
//            BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream("upload" + fileName));
//            stream.write(bytes);
//            stream.close();

            RestTemplate restTemplate = new RestTemplate();
            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            //// if Spring version < 3.1, see https://jira.springsource.org/browse/SPR-7909
            // requestFactory.setBufferRequestBody(false);
            restTemplate.setRequestFactory(requestFactory);

            String url = "http://localhost:8092/files/upload";

            // [resttemplate multipart post](https://jira.spring.io/browse/SPR-13571)
            // [Spring RestTemplate - how to enable full debugging/logging of requests/responses?](https://stackoverflow.com/questions/7952154/spring-resttemplate-how-to-enable-full-debugging-logging-of-requests-responses?rq=1)

            MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
            param.add("file", new FileSystemResource(file));
            param.add("param1", fileName);
            param.add("param2", "Leo");

            HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String,Object>>(param);
            ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);

            //String string = restTemplate.postForObject(url, param, String.class);

            //ResponseEntity e = restTemplate.exchange(url, HttpMethod.POST,
             //       new HttpEntity<Resource>(new FileSystemResource(file)), String.class);

            return responseEntity;
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity("Upload failed", HttpStatus.BAD_REQUEST);
        }
    }

    @RequestMapping("/hello")
    public String hello() {
        return "hello word";
    }
}

1 个答案:

答案 0 :(得分:0)

阅读Spring mvc autowire RequestMappingHandlerMappingGet destination controller from a HttpServletRequest

以下代码有效:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    // https://stackoverflow.com/questions/129207/getting-spring-application-context
    @Autowired
    private org.springframework.context.ApplicationContext appContext;

    private static final String MY_CONTROLLER_PACKAGE_NAME = "nxtcasb.casbproxy";

    @Bean
    protected javax.servlet.Filter proxyFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {

                HandlerMethod handlerMethod = null;
                try {
                    RequestMappingHandlerMapping req2HandlerMapping = (RequestMappingHandlerMapping) appContext.getBean("requestMappingHandlerMapping");
                    // Map<RequestMappingInfo, HandlerMethod> handlerMethods = req2HandlerMapping.getHandlerMethods();
                    HandlerExecutionChain handlerExeChain = req2HandlerMapping.getHandler(request);
                    if (Objects.nonNull(handlerExeChain)) {
                        handlerMethod = (HandlerMethod) handlerExeChain.getHandler();
                        if (handlerMethod.getBeanType().getName().startsWith(MY_CONTROLLER_PACKAGE_NAME)) {
                            filterChain.doFilter(request, response);
                            return;
                        }
                    }
                } catch (Exception e) {
                    logger.warn("Lookup the handler method", e);
                } finally {
                    logger.debug("URI = " + request.getRequestURI() + ", handlerMethod = " + handlerMethod);
                }

                httpProxyForward(request, response);
            }
        };
    }

//    @Bean
//    private FilterRegistrationBean loggingFilterRegistration() {
//        FilterRegistrationBean registration = new FilterRegistrationBean(proxyFilter());
//        registration.addUrlPatterns("/**");
//        return registration;
//    }

    /**
     * this is the same as  <mvc:default-servlet-handler/> <!-- This tag allows for mapping the DispatcherServlet to "/" -->
     *
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //registry.addResourceHandler("/**").addResourceLocations("classpath:/public");
    }

    protected void httpProxyForward(HttpServletRequest request, HttpServletResponse response) {
        HttpClient httpClient = CreateHttpClient();
        HttpUriRequest targetRequest = null;
        HttpResponse targetResponse = null;
        try {
            targetRequest = createHttpUriRequest(request);
            targetResponse = httpClient.execute(targetRequest);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // make sure the entire entity was consumed, so the connection is released
            if (targetResponse != null) {
                EntityUtils.consumeQuietly(targetResponse.getEntity()); // @since 4.2
                //Note: Don't need to close servlet outputStream:
                // http://stackoverflow.com/questions/1159168/should-one-call-close-on-httpservletresponse-getoutputstream-getwriter
            }
        }
    }
}