可以一个 Spring MVC 的 @Bean 检查 HTTP 请求吗?

huangapple go评论107阅读模式
英文:

Can a Spring MVC @Bean inspect the HTTP Request?

问题

以下是翻译好的部分:

我有一个场景,其中Spring应用程序接收带有自定义ContentType和JSON有效载荷的HTTP请求。有效载荷的一部分是用于服务定位器调用的键。

有效载荷:

{ "service_id": "ServiceA" }

我想使用一个@RequestScope的Spring @Bean来检查请求并返回/注入正确的服务。

类似这样的:

@Configuration
public class ServiceLocatorConfig {

    @Bean
    @RequestScope
    public SomeService serviceByKey(WebRequest request, Map<String, SomeService> services) {

        // 这是我需要帮助的地方--我如何从请求体中获取内容?
        String serviceId = readServiceIdFromRequestBody(request);
        return services[serviceId];
    }

}

到目前为止,我在我的@Bean中接收了请求,但是在获取请求体方面一直没有成功。我最接近的尝试是盲目尝试ContentCachingRequestWrapper,但是在调用.getContentAsByteArray()时一直收到null。听起来它还需要一个表单编码的主体--因此,自定义内容类型(在这种情况下无法修改)可能会引起更多麻烦。

Spring Bean如何检查HTTP请求?

英文:

I have a scenario where HTTP requests are received by a Spring application with a custom ContentType and a JSON payload. A piece of the payload is a key used in a service locator call.

Payload:

{ &quot;service_id&quot;: &quot;ServiceA&quot; }

I'd like to use a @RequestScope'd Spring @Bean to inspect the request and return / inject the correct service.

Something like:

@Configuration
public class ServiceLocatorConfig {

    @Bean
    @RequestScope
    public SomeService serviceByKey(WebRequest request, Map&lt;String, SomeService&gt; services) {

        // This is where I need a hand -- how would I get the request body?
        String serviceId = readServiceIdFromRequestBody(request);
        return services[key];
    }

}

So far I'm receiving the request in my @Bean, but haven't had any success getting the request body. The closest I've gotten is by blindly experimenting with ContentCachingRequestWrapper, but I've always received null when calling .getContentAsByteArray(). It also sounds as though it requires a form-encoded body -- so the custom content type (which can't be modified in this case) may be causing more trouble.

How can a Spring Bean inspect a HTTP request?

答案1

得分: 1

为了实现您所需的功能,您可以使用WebFilter,它将作为入站请求的拦截器,在到达您的services之前拦截请求。它将能够访问 JSON 负载,您可以读取传入的参数并调用适当的组件来处理请求。

您的WebFilter类将类似于以下示例(在此示例中,我们拦截到'/rest/*'的请求):

import java.io.IOException;
import java.nio.charset.Charset;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;

@WebFilter(urlPatterns = "/rest/*")
public class DoSomethingInterceptor implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // 解析请求中的 JSON
        String jsonPayload = IOUtils.toString(request.getInputStream(), Charset.defaultCharset());
        IncomingRequest jsonRequest = mapper.readValue(jsonPayload, IncomingRequest.class);
        
        // 读取服务参数
        String serviceClass = jsonRequest.getServiceClass();
        
        // 将传入的服务映射到您的服务类
        Class<MyService> myServiceClass = ServiceType.get(serviceClass);
        
        // 从 Spring 上下文中获取 bean
        DoSomething bean =
                (DoSomething) WebApplicationContextUtils
                        .getRequiredWebApplicationContext(filterConfig.getServletContext())
                        .getBean(myServiceClass);
        
        // 调用服务中的方法
        String resultFromService = bean.doSomething();
        
        // 最后,将服务的结果复制到 HTTP 响应中
        response.getOutputStream().write(resultFromService);
        return;
    }
}

还要记得在 Spring 中注册您的WebFilter,对于 Spring Boot 应用,可以像下面的示例一样完成:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    @Autowired
    public FilterRegistrationBean registerFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DoSomethingInterceptor());
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/rest/*");
        filterRegistration.setUrlPatterns(urlPatterns);
        return filterRegistration;
    }
}
英文:

In order to achieve what you need you can make use of a WebFilter which will work as an interceptor for incoming requests before it reaches your services, it will have access the json payload and you can read the incoming parameter and call the appropriate component to handle the request.

Your WebFilter class will look similar to this one (on this example we are intercepting requests to '/rest/*':

import java.io.IOException;
import java.nio.charset.Charset;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
@WebFilter(urlPatterns = &quot;/rest/*&quot;)
public class DoSomethingInterceptor implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
//Parse the json from request
String jsonPayload = IOUtils.toString(request.getInputStream(), Charset.defaultCharset());
IncomingRequest jsonRequest = mapper.readValue(jsonPayload, IncomingRequest.class);
//Read the service parameter
String serviceClass = jsonRequest.getServiceClass();
//Map the incoming service to your service class
Class&lt;MyService&gt; myServiceClass = ServiceType.get(serviceClass);
//Get the bean from Spring context
DoSomething bean =
(DoSomething) WebApplicationContextUtils.
getRequiredWebApplicationContext(filterConfig.getServletContext()).
getBean(myServiceClass);
//Call the method in the service
String resultFromService = bean.doSomething();
//Finally you will copy the result from service to the http response
response.getOutputStream().write(resultFromService);
return;
}
}

Also remember to register your WebFilter on Spring, for SpringBoot app it can be done like in below example:

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
@Autowired
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new DoSomethingInterceptor());
List&lt;String&gt; urlPatterns = new ArrayList&lt;&gt;();
urlPatterns.add(&quot;/rest/*&quot;);
filterRegistration.setUrlPatterns(urlPatterns);
return filterRegistration;
}

huangapple
  • 本文由 发表于 2020年8月15日 04:53:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/63419897.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定