spring mvc rest 서비스 리다이렉트/전송/프록시
REST 서비스를 공개하기 위해 spring mvc framework를 사용하여 웹 어플리케이션을 만들었습니다.예를 들어 다음과 같습니다.
@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);
}
이제 애플리케이션을 도입해야 하는데 다음과 같은 문제가 있습니다.클라이언트는 응용 프로그램이 있는 컴퓨터에 직접 액세스할 수 없습니다(방화벽이 있습니다).따라서 실제 rest 서비스를 호출하는 프록시 머신(클라이언트가 액세스 가능)의 리다이렉션 레이어가 필요합니다.
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를 사용하려면 컨트롤러의 각 메서드를 다시 작성해야 합니다.또한 이로 인해 프록시 머신에서 용장 시리얼화/디시리얼화가 발생합니다.
re스템plate를 사용하여 범용 함수를 작성하려고 했지만 잘 되지 않았습니다.
@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 주소를 변경하지 않기 때문에 이 경우 무용지물이라고 생각합니다.다른 URL로도 전송할 수 없었습니다.
이것을 실현하기 위한 보다 적절한 방법이 있을까요?
다음을 사용하여 모든 요청을 미러링/프록시할 수 있습니다.
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();
}
이렇게 하면 헤더가 미러링되지 않습니다.
다음은 원본 답변의 수정 버전입니다. 4가지 점에서 다릅니다.
- 요구 본문을 필수로 하지 않기 때문에 GET 요구가 실패하는 일은 없습니다.
- 원래 요청에 있는 모든 헤더를 복사합니다.다른 프록시/웹 서버를 사용하는 경우 콘텐츠 길이/gzip 압축으로 인해 문제가 발생할 수 있습니다.헤더를 필요한 것으로 제한합니다.
- 쿼리 매개 변수 또는 경로는 다시 인코딩되지 않습니다.어쨌든 암호화될 것으로 예상합니다.URL의 다른 부분도 인코딩될 수 있습니다.이 경우, 고객의 비즈니스 요구에 부응하는
UriComponentsBuilder
. - 서버에서 에러 코드가 올바르게 반환됩니다.
@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();
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());
}
}
Netflix Zuul을 사용하여 스프링 응용 프로그램에 들어오는 요청을 다른 스프링 응용 프로그램으로 라우팅할 수 있습니다.
예를 들어 두 가지 어플리케이션이 있다고 합시다.1 . songs - app 、 2. api - gateway
api-gateway 응용 프로그램에서 먼저 zuul dependecy를 추가한 후 application.yml에서 다음과 같이 라우팅 규칙을 정의할 수 있습니다.
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>LATEST</version>
</dependency>
application.yml
server:
port: 8080
zuul:
routes:
foos:
path: /api/songs/**
url: http://localhost:8081/songs/
마지막으로 다음과 같은 API 응용 프로그램을 실행합니다.
@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
기타 자원: http://www.baeldung.com/spring-rest-with-zuul-proxy
@derkoe가 나에게 많은 도움을 준 멋진 답변을 올려주었어!
2021년에 이것을 시도하면서, 조금 개선했습니다.
- 클래스가 @RestController인 경우 @ResponseBody는 필요 없습니다.
- @RequestBody(필수 = false)는 본문 없이 요청을 허용합니다(예: GET).
- SSL 암호화 엔드포인트용 https 및 포트 443(서버가 포트 443에서https를 서비스하는 경우)
- 본문뿐만 아니라 responseEntity 전체를 반환하면 헤더와 응답 코드도 가져옵니다.
- 추가된(선택사항) 헤더 예.
headers.put("Authorization", Arrays.asList(String[] { "Bearer 234asdf234"})
- 예외 처리(500 Server Error를 발생시키지 않고 404와 같은HttpStatus를 캐치하여 전송)
private String server = "localhost";
private int port = 443;
@Autowired
MultiValueMap<String, String> headers;
@Autowired
RestTemplate restTemplate;
@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);
HttpEntity<String> entity = new HttpEntity<>(body, headers);
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;
}
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);
}
mod_downloads와 같은 하위 수준의 솔루션을 사용하는 것이 더 간단하지만, 더 많은 제어(보안, 번역, 비즈니스 로직 등)가 필요한 경우 Apache Camel을 참조하십시오.http://camel.apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html
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());
}
}
이런 게 필요하잖아요jetty transparent proxy
그러면 실제로 콜이 리다이렉트되어 필요에 따라 요청을 덮어쓸 수 있습니다.자세한 것은, http://reanimatter.com/2016/01/25/embedded-jetty-as-http-proxy/ 를 참조해 주세요.
언급URL : https://stackoverflow.com/questions/14726082/spring-mvc-rest-service-redirect-forward-proxy
'programing' 카테고리의 다른 글
WordPress는 사용자 지정 URL을 함수에 매핑합니다. (0) | 2023.04.03 |
---|---|
2개의 필드(하나는 역방향)로 정렬 (0) | 2023.03.29 |
WordPress 검색 결과에 결과 추가 (0) | 2023.03.29 |
AngularJS - HTML 요소 없이 ng-if를 사용하는 방법 (0) | 2023.03.29 |
WordPress Media Library 머리글 이미지 자르기처럼 삽입 시 이미지 자르기 (0) | 2023.03.29 |