commit
						3e49780fa2
					
				@ -1,32 +1,32 @@ | 
				
			||||
* [首页](/?t=1563439736156) | 
				
			||||
* [首页](/?t=1563956863130) | 
				
			||||
* 开发文档 | 
				
			||||
  * [快速体验](files/10010_快速体验.md?t=1563439736157) | 
				
			||||
  * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1563439736174) | 
				
			||||
  * [新增接口](files/10020_新增接口.md?t=1563439736174) | 
				
			||||
  * [业务参数校验](files/10030_业务参数校验.md?t=1563439736174) | 
				
			||||
  * [错误处理](files/10040_错误处理.md?t=1563439736174) | 
				
			||||
  * [编写文档](files/10041_编写文档.md?t=1563439736174) | 
				
			||||
  * [接口交互详解](files/10050_接口交互详解.md?t=1563439736174) | 
				
			||||
  * [easyopen支持](files/10070_easyopen支持.md?t=1563439736175) | 
				
			||||
  * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1563439736175) | 
				
			||||
  * [ISV管理](files/10085_ISV管理.md?t=1563439736175) | 
				
			||||
  * [自定义路由](files/10086_自定义路由.md?t=1563439736175) | 
				
			||||
  * [路由授权](files/10090_路由授权.md?t=1563439736175) | 
				
			||||
  * [接口限流](files/10092_接口限流.md?t=1563439736175) | 
				
			||||
  * [监控日志](files/10093_监控日志.md?t=1563439736175) | 
				
			||||
  * [SDK开发](files/10095_SDK开发.md?t=1563439736175) | 
				
			||||
  * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1563439736176) | 
				
			||||
  * [应用授权](files/10097_应用授权.md?t=1563439736176) | 
				
			||||
  * [更改数据节点名称](files/10099_更改数据节点名称.md?t=1563439736176) | 
				
			||||
  * [对接前端](files/10100_对接前端.md?t=1563439736176) | 
				
			||||
  * [自定义过滤器](files/10102_自定义过滤器.md?t=1563439736176) | 
				
			||||
  * [文件上传](files/10104_文件上传.md?t=1563439736176) | 
				
			||||
  * [nacos注册中心](files/10106_nacos注册中心.md?t=1563439736176) | 
				
			||||
  * [扩展其它注册中心](files/10107_扩展其它注册中心.md?t=1563439736176) | 
				
			||||
  * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1563439736176) | 
				
			||||
  * [快速体验](files/10010_快速体验.md?t=1563956863132) | 
				
			||||
  * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1563956863153) | 
				
			||||
  * [新增接口](files/10020_新增接口.md?t=1563956863153) | 
				
			||||
  * [业务参数校验](files/10030_业务参数校验.md?t=1563956863153) | 
				
			||||
  * [错误处理](files/10040_错误处理.md?t=1563956863153) | 
				
			||||
  * [编写文档](files/10041_编写文档.md?t=1563956863153) | 
				
			||||
  * [接口交互详解](files/10050_接口交互详解.md?t=1563956863153) | 
				
			||||
  * [easyopen支持](files/10070_easyopen支持.md?t=1563956863154) | 
				
			||||
  * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1563956863154) | 
				
			||||
  * [ISV管理](files/10085_ISV管理.md?t=1563956863154) | 
				
			||||
  * [自定义路由](files/10086_自定义路由.md?t=1563956863154) | 
				
			||||
  * [路由授权](files/10090_路由授权.md?t=1563956863154) | 
				
			||||
  * [接口限流](files/10092_接口限流.md?t=1563956863154) | 
				
			||||
  * [监控日志](files/10093_监控日志.md?t=1563956863154) | 
				
			||||
  * [SDK开发](files/10095_SDK开发.md?t=1563956863154) | 
				
			||||
  * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1563956863154) | 
				
			||||
  * [应用授权](files/10097_应用授权.md?t=1563956863155) | 
				
			||||
  * [更改数据节点名称](files/10099_更改数据节点名称.md?t=1563956863155) | 
				
			||||
  * [传统web开发](files/10100_传统web开发.md?t=1563956863155) | 
				
			||||
  * [自定义过滤器](files/10102_自定义过滤器.md?t=1563956863155) | 
				
			||||
  * [文件上传](files/10104_文件上传.md?t=1563956863155) | 
				
			||||
  * [nacos注册中心](files/10106_nacos注册中心.md?t=1563956863155) | 
				
			||||
  * [扩展其它注册中心](files/10107_扩展其它注册中心.md?t=1563956863155) | 
				
			||||
  * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1563956863155) | 
				
			||||
* 原理分析 | 
				
			||||
  * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1563439736177) | 
				
			||||
  * [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1563439736177) | 
				
			||||
  * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1563439736177) | 
				
			||||
  * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1563439736177) | 
				
			||||
  * [常见问题](files/90100_常见问题.md?t=1563439736177) | 
				
			||||
  * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1563956863156) | 
				
			||||
  * [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1563956863156) | 
				
			||||
  * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1563956863156) | 
				
			||||
  * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1563956863156) | 
				
			||||
  * [常见问题](files/90100_常见问题.md?t=1563956863156) | 
				
			||||
 | 
				
			||||
@ -1,41 +1,40 @@ | 
				
			||||
# 对接前端 | 
				
			||||
# 传统web开发 | 
				
			||||
 | 
				
			||||
开放平台一般对接服务端应用,开发者使用SDK来调用接口。 | 
				
			||||
理论上来说,只要是客户端程序,都可以调用网关接口。因此,同样可以对接前端应用(H5、小程序、App)。 | 
				
			||||
本篇介绍如何使用SOP进行传统web服务开发,即对接前端应用(H5、小程序、App)。 | 
				
			||||
 | 
				
			||||
针对H5页面,配置方式如下: | 
				
			||||
 | 
				
			||||
- 网关关闭签名校验。`ApiConfig.getInstance().setIgnoreValidate(true);` | 
				
			||||
 | 
				
			||||
由于是网页调用接口,把秘钥放在前端意义不大,干脆直接关闭签名校验。 | 
				
			||||
 | 
				
			||||
- 设置统一的数据节点 | 
				
			||||
- 网关ZuulConfig继承WebappZuulConfiguration类 | 
				
			||||
 | 
				
			||||
```java | 
				
			||||
@Configuration | 
				
			||||
public class ZuulConfig extends AlipayZuulConfiguration { | 
				
			||||
public class ZuulConfig extends WebappZuulConfiguration { | 
				
			||||
 | 
				
			||||
    static { | 
				
			||||
        ... | 
				
			||||
        ApiConfig.getInstance().setIgnoreValidate(true); | 
				
			||||
        ApiConfig.getInstance().setDataNameBuilder(new CustomDataNameBuilder()); | 
				
			||||
        ... | 
				
			||||
        new ManagerInitializer(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
设置后,网关统一的返回结果如下: | 
				
			||||
设置完毕,网关不在进行签名验证,网关统一的返回结果如下: | 
				
			||||
 | 
				
			||||
```json | 
				
			||||
{ | 
				
			||||
	"result": { | 
				
			||||
		... | 
				
			||||
	}, | 
				
			||||
	"sign": "xxxxx" | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
- 微服务OpenServiceConfig继承WebappServiceConfiguration类 | 
				
			||||
 | 
				
			||||
```java | 
				
			||||
public class OpenServiceConfig extends WebappServiceConfiguration { | 
				
			||||
    ... | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
其它内容不变 | 
				
			||||
 | 
				
			||||
- 封装请求工具【可选】 | 
				
			||||
 | 
				
			||||
封装请求,方便调用,针对vue的封装如下: | 
				
			||||
@ -0,0 +1,18 @@ | 
				
			||||
package com.gitee.sop.gatewaycommon.zuul.configuration; | 
				
			||||
 | 
				
			||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig; | 
				
			||||
import com.gitee.sop.gatewaycommon.result.CustomDataNameBuilder; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 支持传统webapp开发,没有签名验证 | 
				
			||||
 * | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
public class WebappZuulConfiguration extends BaseZuulConfiguration { | 
				
			||||
 | 
				
			||||
    static { | 
				
			||||
        ApiConfig.getInstance().setIgnoreValidate(true); | 
				
			||||
        ApiConfig.getInstance().setDataNameBuilder(new CustomDataNameBuilder()); | 
				
			||||
        ApiConfig.getInstance().setShowReturnSign(false); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,26 +1,25 @@ | 
				
			||||
package com.gitee.sop.gatewaycommon.zuul.filter; | 
				
			||||
 | 
				
			||||
import com.gitee.sop.gatewaycommon.util.RequestUtil; | 
				
			||||
import com.netflix.zuul.context.RequestContext; | 
				
			||||
import org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter; | 
				
			||||
 | 
				
			||||
import javax.servlet.http.HttpServletRequest; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
public class FormBodyWrapperFilterExt extends FormBodyWrapperFilter { | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    public boolean shouldFilter() { | 
				
			||||
        RequestContext ctx = RequestContext.getCurrentContext(); | 
				
			||||
        HttpServletRequest request = ctx.getRequest(); | 
				
			||||
        // 如果是文件上传请求,不需要包装
 | 
				
			||||
        if (RequestUtil.isMultipart(request)) { | 
				
			||||
            return false; | 
				
			||||
        } else { | 
				
			||||
            return super.shouldFilter(); | 
				
			||||
        } | 
				
			||||
    public int filterOrder() { | 
				
			||||
        return BaseZuulFilter.FORM_BODY_WRAPPER_FILTER_ORDER; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    //    @Override
 | 
				
			||||
//    public boolean shouldFilter() {
 | 
				
			||||
//        RequestContext ctx = RequestContext.getCurrentContext();
 | 
				
			||||
//        HttpServletRequest request = ctx.getRequest();
 | 
				
			||||
//        // 如果是文件上传请求,不需要包装
 | 
				
			||||
//        if (RequestUtil.isMultipart(request)) {
 | 
				
			||||
//            return false;
 | 
				
			||||
//        } else {
 | 
				
			||||
//            return super.shouldFilter();
 | 
				
			||||
//        }
 | 
				
			||||
//    }
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,31 @@ | 
				
			||||
package com.gitee.sop.gatewaycommon.zuul.filter; | 
				
			||||
 | 
				
			||||
import com.netflix.zuul.context.RequestContext; | 
				
			||||
import com.netflix.zuul.exception.ZuulException; | 
				
			||||
import com.netflix.zuul.http.HttpServletRequestWrapper; | 
				
			||||
 | 
				
			||||
import javax.servlet.http.HttpServletRequest; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 包装一下Request,使得request.getInputStream()方法可以调用多次 | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
public class PreHttpServletRequestWrapperFilter extends BaseZuulFilter { | 
				
			||||
    @Override | 
				
			||||
    protected FilterType getFilterType() { | 
				
			||||
        return FilterType.PRE; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    protected int getFilterOrder() { | 
				
			||||
        return HTTP_SERVLET_REQUEST_WRAPPER_FILTER_ORDER; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Override | 
				
			||||
    protected Object doRun(RequestContext requestContext) throws ZuulException { | 
				
			||||
        HttpServletRequest request = requestContext.getRequest(); | 
				
			||||
        HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request); | 
				
			||||
        requestContext.setRequest(wrapper); | 
				
			||||
        return null; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,24 +1,25 @@ | 
				
			||||
package com.gitee.sop.gatewaycommon.zuul.filter; | 
				
			||||
 | 
				
			||||
import com.gitee.sop.gatewaycommon.util.RequestUtil; | 
				
			||||
import com.netflix.zuul.context.RequestContext; | 
				
			||||
import org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter; | 
				
			||||
 | 
				
			||||
import javax.servlet.http.HttpServletRequest; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
public class Servlet30WrapperFilterExt extends Servlet30WrapperFilter { | 
				
			||||
    @Override | 
				
			||||
    public boolean shouldFilter() { | 
				
			||||
        RequestContext ctx = RequestContext.getCurrentContext(); | 
				
			||||
        HttpServletRequest request = ctx.getRequest(); | 
				
			||||
        // 如果是文件上传请求,不需要包装
 | 
				
			||||
        if (RequestUtil.isMultipart(request)) { | 
				
			||||
            return false; | 
				
			||||
        } else { | 
				
			||||
            return super.shouldFilter(); | 
				
			||||
        } | 
				
			||||
    public int filterOrder() { | 
				
			||||
        return BaseZuulFilter.SERVLET_30_WRAPPER_FILTER_ORDER; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    //    @Override
 | 
				
			||||
//    public boolean shouldFilter() {
 | 
				
			||||
//        RequestContext ctx = RequestContext.getCurrentContext();
 | 
				
			||||
//        HttpServletRequest request = ctx.getRequest();
 | 
				
			||||
//        // 如果是文件上传请求,不需要包装
 | 
				
			||||
//        if (RequestUtil.isMultipart(request)) {
 | 
				
			||||
//            return false;
 | 
				
			||||
//        } else {
 | 
				
			||||
//            return super.shouldFilter();
 | 
				
			||||
//        }
 | 
				
			||||
//    }
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,14 @@ | 
				
			||||
package com.gitee.sop.servercommon.configuration; | 
				
			||||
 | 
				
			||||
import com.gitee.sop.servercommon.bean.ServiceConfig; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 通用web | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
public class WebappServiceConfiguration extends BaseServiceConfiguration { | 
				
			||||
    static { | 
				
			||||
        // 默认版本号为1.0
 | 
				
			||||
        ServiceConfig.getInstance().setDefaultVersion("1.0"); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,12 +1,11 @@ | 
				
			||||
package com.gitee.sop.servercommon.param; | 
				
			||||
 | 
				
			||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | 
				
			||||
 | 
				
			||||
import java.util.List; | 
				
			||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
public interface SopHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver { | 
				
			||||
    void setResolvers(List<HandlerMethodArgumentResolver> resolvers); | 
				
			||||
    void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,94 @@ | 
				
			||||
package com.gitee.sop.storyweb.controller; | 
				
			||||
 | 
				
			||||
import com.gitee.sop.servercommon.annotation.ApiMapping; | 
				
			||||
import com.gitee.sop.servercommon.util.UploadUtil; | 
				
			||||
import lombok.Data; | 
				
			||||
import org.springframework.web.bind.annotation.RequestBody; | 
				
			||||
import org.springframework.web.bind.annotation.RequestMapping; | 
				
			||||
import org.springframework.web.bind.annotation.RequestMethod; | 
				
			||||
import org.springframework.web.bind.annotation.RestController; | 
				
			||||
import org.springframework.web.multipart.MultipartFile; | 
				
			||||
 | 
				
			||||
import javax.servlet.http.HttpServletRequest; | 
				
			||||
import java.math.BigDecimal; | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * 传统web开发实例 | 
				
			||||
 * @author tanghc | 
				
			||||
 */ | 
				
			||||
@RestController | 
				
			||||
@RequestMapping("food") | 
				
			||||
public class TraditionalWebappController { | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    // http://localhost:8081/getFoodById?id=1  网关入口
 | 
				
			||||
    // http://localhost:2222/food/getFoodById/?id=12  本地入口
 | 
				
			||||
    @ApiMapping(value = "getFoodById", method = RequestMethod.GET) | 
				
			||||
    public Food getFoodById(Integer id) { | 
				
			||||
        Food food = new Food(); | 
				
			||||
        food.setId(id); | 
				
			||||
        food.setName("香蕉"); | 
				
			||||
        food.setPrice(new BigDecimal(20.00)); | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // http://localhost:8081/getFoodById?id=2&version=1.1   网关入口
 | 
				
			||||
    // http://localhost:2222/food/getFoodById/?id=12&version=1.1
 | 
				
			||||
    @ApiMapping(value = "getFoodById", method = RequestMethod.GET, version = "1.1") | 
				
			||||
    public Food getFoodById2(Integer id) { | 
				
			||||
        Food food = new Food(); | 
				
			||||
        food.setId(id); | 
				
			||||
        food.setName("香蕉2"); | 
				
			||||
        food.setPrice(new BigDecimal(22.00)); | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // http://localhost:8081/getFoodByObj?id=2
 | 
				
			||||
    @ApiMapping(value = "getFoodByObj", method = RequestMethod.GET) | 
				
			||||
    public Food getFoodByObj(Food food) { | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    // http://localhost:8081/saveFood
 | 
				
			||||
    @ApiMapping(value = "saveFood", method = RequestMethod.POST) | 
				
			||||
    public Food saveFood(@RequestBody Food food) { | 
				
			||||
        food.setId(3); | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @ApiMapping(value = "foodUpload", method = RequestMethod.POST) | 
				
			||||
    public Food upload(Food food, HttpServletRequest request) { | 
				
			||||
        // 获取上传的文件
 | 
				
			||||
        Collection<MultipartFile> uploadFiles = UploadUtil.getUploadFiles(request); | 
				
			||||
        StringBuilder sb = new StringBuilder(); | 
				
			||||
        for (MultipartFile multipartFile : uploadFiles) { | 
				
			||||
            sb.append(multipartFile.getOriginalFilename()); | 
				
			||||
        } | 
				
			||||
        food.setId(4); | 
				
			||||
        food.setName("文件名称+" + sb.toString()); | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @ApiMapping(value = "foodUpload3", method = RequestMethod.POST) | 
				
			||||
    public Food upload3(Food food, MultipartFile file) { | 
				
			||||
        food.setId(5); | 
				
			||||
        food.setName("文件名称+" + file.getOriginalFilename()); | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @RequestMapping(value = "foodUpload2", method = RequestMethod.POST) | 
				
			||||
    public Food upload2(Food food, MultipartFile file) { | 
				
			||||
        food.setId(4); | 
				
			||||
        food.setName("文件名称2+" + file.getOriginalFilename()); | 
				
			||||
        return food; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    @Data | 
				
			||||
    public static class Food { | 
				
			||||
        private Integer id; | 
				
			||||
        private String name; | 
				
			||||
        private BigDecimal price; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue