我使用spring mvc框架构建了一个Web应用程序来发布REST服务。 例如:
@Controller
@RequestMapping("/movie")
public class MovieController {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public @ResponseBody Movie getMovie(@PathVariable String id, @RequestBody user) {
return dataProvider.getMovieById(user,id);
}
现在我需要部署我的应用程序但是我有以下问题: 客户端无法直接访问应用程序所在的计算机(有防火墙)。因此,我需要在代理机器上(可由客户端访问)调用实际的休息服务。
我尝试使用RestTemplate进行新的调用: 例如:
@Controller
@RequestMapping("/movieProxy")
public class MovieProxyController {
private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public @ResponseBody Movie getMovie(@PathVariable String id,@RequestBody user,final HttpServletResponse response,final HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), new HttpEntity<T>(user, headers), Movie.class);
}
这没关系,但我需要重写控制器中的每个方法以使用resttemplate。此外,这会导致代理计算机上的冗余序列化/反序列化。
我尝试使用restemplate编写泛型函数,但它没有用完:
@Controller
@RequestMapping("/movieProxy")
public class MovieProxyController {
private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";
@RequestMapping(value = "/**")
public ? redirect(final HttpServletResponse response,final HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), ? , ?);
}
我找不到一个与请求和响应对象一起使用的resttemplate方法。
我也尝试过弹簧重定向和前进。但重定向不会改变请求的客户端IP地址,所以我认为在这种情况下它是无用的。我也无法转发到其他网址。
有没有更合适的方法来实现这一目标? 提前谢谢。
答案 0 :(得分:52)
您可以使用以下方式镜像/代理所有请求:
private String server = "localhost";
private int port = 8080;
@RequestMapping("/**")
@ResponseBody
public String mirrorRest(@RequestBody String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
URI uri = new URI("http", null, server, port, request.getRequestURI(), request.getQueryString(), null);
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, new HttpEntity<String>(body), String.class);
return responseEntity.getBody();
}
这不会镜像任何标题。
答案 1 :(得分:14)
您可以使用Netflix Zuul将发送到spring应用程序的请求路由到另一个spring应用程序。
我们假设您有两个应用程序:1.songs-app,2.api-gateway
在api-gateway应用程序中,首先添加zuul依赖,然后您可以在application.yml中简单地定义路由规则,如下所示:
的pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>LATEST</version>
</dependency>
application.yml
server:
port: 8080
zuul:
routes:
foos:
path: /api/songs/**
url: http://localhost:8081/songs/
最后运行api-gateway应用程序,如:
@EnableZuulProxy
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
现在,网关会将所有/api/songs/
个请求路由到http://localhost:8081/songs/
。
这里有一个工作示例:https://github.com/muatik/spring-playground/tree/master/spring-api-gateway
答案 2 :(得分:7)
这是我原始答案的修改版本,它有四点不同:
// Schedule JobScheduler job
ComponentName serviceComponent = new ComponentName(context, UploadJobService.class);
JobInfo job = new JobInfo.Builder(1, serviceComponent)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.build();
JobScheduler mJobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
mJobScheduler.schedule(job);
。
UriComponentsBuilder
答案 3 :(得分:1)
如果您可以使用mod_proxy等较低级别的解决方案,这将是更简单的方法,但如果您需要更多控制(例如安全性,翻译,业务逻辑),您可能需要查看Apache骆驼:http://camel.apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html
答案 4 :(得分:1)
具有oauth2的代理控制器
@RequestMapping("v9")
@RestController
@EnableConfigurationProperties
public class ProxyRestController {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;
@Autowired
private ClientCredentialsResourceDetails clientCredentialsResourceDetails;
@Autowired
OAuth2RestTemplate oAuth2RestTemplate;
@Value("${gateway.url:http://gateway/}")
String gatewayUrl;
@RequestMapping(value = "/proxy/**")
public String proxy(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response,
@RequestHeader HttpHeaders headers) throws ServletException, IOException, URISyntaxException {
body = body == null ? "" : body;
String path = request.getRequestURI();
String query = request.getQueryString();
path = path.replaceAll(".*/v9/proxy", "");
StringBuffer urlBuilder = new StringBuffer(gatewayUrl);
if (path != null) {
urlBuilder.append(path);
}
if (query != null) {
urlBuilder.append('?');
urlBuilder.append(query);
}
URI url = new URI(urlBuilder.toString());
if (logger.isInfoEnabled()) {
logger.info("url: {} ", url);
logger.info("method: {} ", method);
logger.info("body: {} ", body);
logger.info("headers: {} ", headers);
}
ResponseEntity<String> responseEntity
= oAuth2RestTemplate.exchange(url, method, new HttpEntity<String>(body, headers), String.class);
return responseEntity.getBody();
}
@Bean
@ConfigurationProperties("security.oauth2.client")
@ConditionalOnMissingBean(ClientCredentialsResourceDetails.class)
public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
@Bean
@ConditionalOnMissingBean
public OAuth2RestTemplate oAuth2RestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails);
}
答案 5 :(得分:1)
我受到了 Veluria 解决方案的启发,但我遇到了从目标资源发送的 gzip 压缩问题。
目标是省略 Accept-Encoding
标头:
@RequestMapping("/**")
public ResponseEntity mirrorRest(@RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!headerName.equals("Accept-Encoding")) {
headers.set(headerName, request.getHeader(headerName));
}
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
答案 6 :(得分:0)
您需要类似jetty transparent proxy
之类的东西,它实际上将重定向您的呼叫,并且您有机会在需要时覆盖请求。您可以在http://reanimatter.com/2016/01/25/embedded-jetty-as-http-proxy/
答案 7 :(得分:0)
@derkoe 发布了一个很好的答案,对我帮助很大!
在 2021 年尝试此方法后,我对其进行了一些改进:
headers.put("Authorization", Arrays.asList(String[] { "Bearer 234asdf234"})
private String server = "localhost";
private int port = 443;
@Autowired
MultiValueMap<String, String> headers;
@RequestMapping("/**")
public ResponseEntity<String> mirrorRest(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
URI uri = new URI("https", null, server, port, request.getRequestURI(), request.getQueryString(), null);
try {
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, entity, String.class);
return responseEntity;
} catch (HttpClientErrorException ex) {
return ResponseEntity
.status(ex.getStatusCode())
.headers(ex.getResponseHeaders())
.body(ex.getResponseBodyAsString());
}
return responseEntity;
}