为什么会有跨域问题
因为浏览器的同源政策,就会产生跨域。比如说发送的异步请求是不同的两个源,就比如是不同的的两个端口或者不同的两个协议或者不同的域名。由于浏览器为了安全考虑,就会产生一个同源政策,不是同一个地方出来的是不允许进行交互的。
常见的跨域解决方式
  1. 在控制层加入允许跨域的注解 @CrossOrigin
  2. 使用httpclient,不依赖浏览器
  3. 使用网关 Gateway

注解:@CrossOrigin

在控制层加入允许跨域的注解,即可完成一个项目中前后端口跨域的问题
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

网关整合

Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其 不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监 控/埋点、限流等。
(1)路由
路由是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言和一组 Filter组成。如果断言路由为真,则说明请求的URL和配置匹配
(2)断言
Java8中的断言函数。Spring Cloud Gateway中的断言函数输入类型是Spring5.0框 架中的ServerWebExchangeSpring Cloud Gateway中的断言函数允许开发者去定义匹配来自 于http request中的任何信息,比如请求头和参数等。
(3)过滤器
一个标准的Spring webFilterSpring cloud gateway中的filter分为两种类型的 Filter,分别是Gateway FilterGlobal Filter。过滤器Filter将会对请求和响应进行修改处理
Spring cloud Gateway发出请求。然后再由Gateway Handler Mapping中找到与请 求相匹配的路由,将其发送到Gateway web handler。Handler再通过指定的过滤器链将请求发 送到实际的服务执行业务逻辑,然后返回。
项目中使用
新建模块service_gateway
<dependencies>
<!-- 公共模块依赖 -->
<dependency>
<groupId>
com.lzq
</groupId>
<artifactId>
service_utils
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-gateway
</artifactId>
</dependency>
<!-- 服务注册 -->
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-discovery
</artifactId>
</dependency>
</dependencies>
配置文件
#服务端口

server.port=9090


> 基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

>

> * 项目地址:<https://gitee.com/zhijiantianya/yudao-cloud>

> * 视频教程:<https://doc.iocoder.cn/video/>


# 服务名

spring.application.name=service-gateway

# nacos服务地址 默认8848

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8888

#使用服务发现路由

spring.cloud.gateway.discovery.locator.enabled=true

#设置路由id

spring.cloud.gateway.routes[0].id=service-hosp

#设置路由的uri lb负载均衡

spring.cloud.gateway.routes[0].uri=lb://service-hosp

#设置路由断言,代理servicerId为auth-service的/auth/路径

spring.cloud.gateway.routes[0].predicates= Path=/*/hosp/**


#设置路由id

spring.cloud.gateway.routes[1].id=service-cmn

#设置路由的uri

spring.cloud.gateway.routes[1].uri=lb://service-cmn

#设置路由断言,代理servicerId为auth-service的/auth/路径

spring.cloud.gateway.routes[1].predicates= Path=/*/cmn/**

#设置路由id

spring.cloud.gateway.routes[2].id=service-hosp

#设置路由的uri

spring.cloud.gateway.routes[2].uri=lb://service-hosp

#设置路由断言,代理servicerId为auth-service的/auth/路径

spring.cloud.gateway.routes[2].predicates= Path=/*/userlogin/**

创建启动类
@SpringBootApplication
publicclassApiGatewayApplication
{

publicstaticvoidmain(String[] args)
{

        SpringApplication.run(ApiGatewayApplication
.classargs)
;

    }

}

修改前端.evn文件,改成访问网关端口号
做集群部署时,他会根据名称实现负载均衡
跨域理解:发送请求后,网关过滤器会进行请求拦截,将跨域放行,转发到服务器中
跨域配置类
@Configuration
publicclassCorsConfig
{

@Bean
public CorsWebFilter corsFilter()
{

        CorsConfiguration config = 
new
 CorsConfiguration();

        config.addAllowedMethod(
"*"
);

        config.addAllowedOrigin(
"*"
);

        config.addAllowedHeader(
"*"
);


        UrlBasedCorsConfigurationSource source = 
new
 UrlBasedCorsConfigurationSource(
new
 PathPatternParser());

        source.registerCorsConfiguration(
"/**"
, config);


returnnew
 CorsWebFilter(source);

    }

}

若之前采用注解跨域,需要将@CrossOrigin去掉

Httpclient

常见的使用场景:多系统之间接口的交互、爬虫
http原生请求,获取百度首页代码
publicclassHttpTest
{

@Test
publicvoidtest1()throws Exception 
{

     String url = 
"https://www.badu.com"
;

        URL url1 = 
new
 URL(url);

//url连接
        URLConnection urlConnection = url1.openConnection();

        HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection;

//获取httpURLConnection输入流
        InputStream is = httpURLConnection.getInputStream();

//转换为字符串
        InputStreamReader reader = 
new
 InputStreamReader(is, StandardCharsets.UTF_8);

        BufferedReader br = 
new
 BufferedReader(reader);

        String line;

//将字符串一行一行读取出来
while
 ((line = br.readLine())!= 
null
){

            System.out.println(line);

        }

    }

}

//设置请求类型
httpURLConnection.setRequestMethod(
"GET"
);

//请求包含 请求行、空格、请求头、请求体
//设置请求头编码
httpURLConnection.setRequestProperty(
"Accept-Charset"
,
"utf-8"
);

使用HttpClient发送请求、接收响应
  1. 创建HttpClient对象。
  2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
  3. 如果需要发送请求参数,可调用HttpGetHttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
  4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse
  5. 调用HttpResponsegetAllHeaders()getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponsegetEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
  6. 释放连接。无论执行方法是否成功,都必须释放连接
集成测试,添加依赖
<dependency>

    <groupId>org.apache.httpcomponents</groupId>

    <artifactId>httpclient</artifactId>

    <version>
4.5.13
</version>

</dependency>

@Test
publicvoidtest2()
{

//可关闭的httpclient客户端,相当于打开一个浏览器
    CloseableHttpClient client = HttpClients.createDefault();

    String url = 
"https://www.baidu.com"
;

//构造httpGet请求对象
    HttpGet httpGet = 
new
 HttpGet(url);

//响应
    CloseableHttpResponse response = 
null
;

try
 {

        response = client.execute(httpGet);

// 获取内容
        String result = EntityUtils.toString(response.getEntity(), 
"utf-8"
);

        System.out.println(result);

    } 
catch
 (Exception e) {

        e.printStackTrace();

    }
finally
 {

//关闭流
if
 (client != 
null
){

try
 {

                client.close();

            } 
catch
 (IOException e) {

                e.printStackTrace();

            }

        }

    }

}

项目中使用,系统调用平台接口保存信息,根据传入josn数据保存信息
系统中
@RequestMapping
(value=
"/hospital/save"
,method=RequestMethod.POST)

public String saveHospital(String data, HttpServletRequest request)
{

try
 {

  apiService.saveHospital(data);

 } 
catch
 (YyghException e) {

returnthis
.failurePage(e.getMessage(),request);

 } 
catch
 (Exception e) {

returnthis
.failurePage(
"数据异常"
,request);

 }

returnthis
.successPage(
null
,request);

}

saveHospital方法
@Override
publicbooleansaveHospital(String data)
{

    JSONObject jsonObject = JSONObject.parseObject(data);

    Map<String, Object> paramMap = 
new
 HashMap<>();

    paramMap.put(
"hoscode"
,
"10000"
);

    paramMap.put(
"hosname"
,jsonObject.getString(
"hosname"
))

//图片
    paramMap.put(
"logoData"
, jsonObject.getString(
"logoData"
));

//  http://localhost:8201/api/hosp/saveHospital
//httpclient
    JSONObject respone =

            HttpRequestHelper.sendRequest(paramMap,
this
.getApiUrl()+
"/api/hosp/saveHospital"
);

    System.out.println(respone.toJSONString());


if
(
null
 != respone && 
200
 == respone.getIntValue(
"code"
)) {

returntrue
;

    } 
else
 {

thrownew
 YyghException(respone.getString(
"message"
), 
201
);

    }

}

HttpRequestHelper工具类
/**

 * 封装同步请求

 * 
@param
 paramMap

 * 
@param
 url

 * 
@return
 */

publicstatic JSONObject sendRequest(Map<String, Object> paramMap, String url)
{

    String result = 
""
;

try
 {

//封装post参数
        StringBuilder postdata = 
new
 StringBuilder();

for
 (Map.Entry<String, Object> param : paramMap.entrySet()) {

            postdata.append(param.getKey()).append(
"="
)

                    .append(param.getValue()).append(
"&"
);

        }

        log.info(String.format(
"--> 发送请求:post data %1s"
, postdata));

byte
[] reqData = postdata.toString().getBytes(
"utf-8"
);

byte
[] respdata = HttpUtil.doPost(url,reqData);

        result = 
new
 String(respdata);

        log.info(String.format(
"--> 应答结果:result data %1s"
, result));

    } 
catch
 (Exception ex) {

        ex.printStackTrace();

    }

return
 JSONObject.parseObject(result);

}

HttpUtil工具类
publicstaticbyte
[] send(String strUrl, String reqmethod, 
byte
[] reqData) {

try
 {

   URL url = 
new
 URL(strUrl);

   HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();

   httpcon.setDoOutput(
true
);

   httpcon.setDoInput(
true
);

   httpcon.setUseCaches(
false
);

   httpcon.setInstanceFollowRedirects(
true
);

   httpcon.setConnectTimeout(CONN_TIMEOUT);

   httpcon.setReadTimeout(READ_TIMEOUT);

   httpcon.setRequestMethod(reqmethod);

   httpcon.connect();

if
 (reqmethod.equalsIgnoreCase(POST)) {

    OutputStream os = httpcon.getOutputStream();

    os.write(reqData);

    os.flush();

    os.close();

   }

   BufferedReader in = 
new
 BufferedReader(
new
 InputStreamReader(httpcon.getInputStream(),
"utf-8"
));

   String inputLine;

   StringBuilder bankXmlBuffer = 
new
 StringBuilder();

while
 ((inputLine = in.readLine()) != 
null
) {  

       bankXmlBuffer.append(inputLine);  

   }  

   in.close();  

   httpcon.disconnect();

return
 bankXmlBuffer.toString().getBytes();

  } 
catch
 (Exception ex) {

   log.error(ex.toString(), ex);

returnnull
;

  }

 }

对应平台接口
@RestController
@RequestMapping
(
"/api/hosp"
)

publicclassApiController
{

@Autowired
private
 HospitalService hospitalService;

@ApiOperation
(value = 
"上传医院"
)

@PostMapping
(
"saveHospital"
)

public R saveHospital(HttpServletRequest request)
{

//通过request取到前端接口传过来的值
        Map<String, String[]> parameterMap = request.getParameterMap();

//将数组值转换成一个值
        Map<String, Object> paramMap = HttpRequestHelper.switchMap(parameterMap);

//将map集合转成josn字符串
        String mapStr = JSONObject.toJSONString(paramMap);

//josn字符串转成对象
        Hospital hospital = JSONObject.parseObject(mapStr, Hospital
.class)
;

//加入MongoDB中
        hospitalService.saveHosp(hospital);

return
 R.ok();

    }

}

即可完成不同系统中的相互调用


欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,
长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。
提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
继续阅读
阅读原文