一步一步教你在SpringBoot中集成微信扫码支付
toyiye 2024-09-16 06:02 3 浏览 0 评论
一:准备工作
使用微信支付需要先开通服务号,然后还要开通微信支付,最后还要配置一些开发参数,过程比较多。
- 申请服务号(企业)
- 开通微信支付
- 开发配置 具体准备工作请参考Spring Boot入门教程(三十九):微信支付集成-申请服务号和微信支付
二:开发文档
扫码支付有两种模式,分为模式一、模式二,模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数codeurl,商户后台系统将codeurl值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。
业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。
扫码支付文档
三:集成步骤
1. 引入依赖
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
2. application.yml
pay:
wxpay:
appID: xxx
mchID: xxx
key: xxx
sandboxKey: xxx
certPath: /var/local/cert/apiclient_cert.p12
useSandbox: false
3. MyWXPayConfig
/**
* 微信支付的参数配置
*
* @author mengday zhang
*/
@Data
@Slf4j
@ConfigurationProperties(prefix = "pay.wxpay")
public class MyWXPayConfig implements WXPayConfig{
/** 公众账号ID */
private String appID;
/** 商户号 */
private String mchID;
/** API 密钥 */
private String key;
/** API 沙箱环境密钥 */
private String sandboxKey;
/** API证书绝对路径 */
private String certPath;
/** 退款异步通知地址 */
private String notifyUrl;
private Boolean useSandbox;
/** HTTP(S) 连接超时时间,单位毫秒 */
private int httpConnectTimeoutMs = 8000;
/** HTTP(S) 读数据超时时间,单位毫秒 */
private int httpReadTimeoutMs = 10000;
/**
* 获取商户证书内容
*
* @return 商户证书内容
*/
@Override
public InputStream getCertStream() {
File certFile = new File(certPath);
InputStream inputStream = null;
try {
inputStream = new FileInputStream(certFile);
} catch (FileNotFoundException e) {
log.error("cert file not found, path={}, exception is:{}", certPath, e);
}
return inputStream;
}
@Override
public String getKey(){
if (useSandbox) {
return sandboxKey;
}
return key;
}
}
4. WXPayClient
/**
* WXPayClient
* <p>
* 对WXPay的简单封装,处理支付密切相关的逻辑.
*
* @author Mengday Zhang
* @version 1.0
* @since 2018/6/16
*/
@Slf4j
public class WXPayClient extends WXPay {
/** 密钥算法 */
private static final String ALGORITHM = "AES";
/** 加解密算法/工作模式/填充方式 */
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
/** 用户支付中,需要输入密码 */
private static final String ERR_CODE_USERPAYING = "USERPAYING";
private static final String ERR_CODE_AUTHCODEEXPIRE = "AUTHCODEEXPIRE";
/** 交易状态: 未支付 */
private static final String TRADE_STATE_NOTPAY = "NOTPAY";
/** 用户输入密码,尝试30秒内去查询支付结果 */
private static Integer remainingTimeMs = 10000;
private WXPayConfig config;
public WXPayClient(WXPayConfig config, WXPayConstants.SignType signType, boolean useSandbox) {
super(config, signType, useSandbox);
this.config = config;
}
/**
*
* 刷卡支付
*
* 对WXPay#microPay(Map)增加了当支付结果为USERPAYING时去轮询查询支付结果的逻辑处理
*
* 注意:该方法没有处理return_code=FAIL的情况,暂时不考虑网络问题,这种情况直接返回错误
*
* @param reqData
* @return
* @throws Exception
*/
public Map<String, String> microPayWithPOS(Map<String, String> reqData) throws Exception {
// 开始时间(毫秒)
long startTimestampMs = System.currentTimeMillis();
Map<String, String> responseMapForPay = super.microPay(reqData);
log.info(responseMapForPay.toString());
// // 先判断 协议字段返回(return_code),再判断 业务返回,最后判断 交易状态(trade_state)
// 通信标识,非交易标识
String returnCode = responseMapForPay.get("return_code");
if (WXPayConstants.SUCCESS.equals(returnCode)) {
String errCode = responseMapForPay.get("err_code");
// 余额不足,信用卡失效
if (ERR_CODE_USERPAYING.equals(errCode) || "SYSTEMERROR".equals(errCode) || "BANKERROR".equals(errCode)) {
Map<String, String> orderQueryMap = null;
Map<String, String> requestData = new HashMap<>();
requestData.put("out_trade_no", reqData.get("out_trade_no"));
// 用户支付中,需要输入密码或系统错误则去重新查询订单API err_code, result_code, err_code_des
// 每次循环时的当前系统时间 - 开始时记录的时间 > 设定的30秒时间就退出
while (System.currentTimeMillis() - startTimestampMs < remainingTimeMs) {
// 商户收银台得到USERPAYING状态后,经过商户后台系统调用【查询订单API】查询实际支付结果。
orderQueryMap = super.orderQuery(requestData);
String returnCodeForQuery = orderQueryMap.get("return_code");
if (WXPayConstants.SUCCESS.equals(returnCodeForQuery)) {
// 通讯成功
String tradeState = orderQueryMap.get("trade_state");
if (WXPayConstants.SUCCESS.equals(tradeState)) {
// 如果成功了直接将查询结果返回
return orderQueryMap;
}
// 如果支付结果仍为USERPAYING,则每隔5秒循环调用【查询订单API】判断实际支付结果
Thread.sleep(1000);
}
}
// 如果用户取消支付或累计30秒用户都未支付,商户收银台退出查询流程后继续调用【撤销订单API】撤销支付交易。
String tradeState = orderQueryMap.get("trade_state");
if (TRADE_STATE_NOTPAY.equals(tradeState) || ERR_CODE_USERPAYING.equals(tradeState) || ERR_CODE_AUTHCODEEXPIRE.equals(tradeState)) {
Map<String, String> reverseMap = this.reverse(requestData);
String returnCodeForReverse = reverseMap.get("return_code");
String resultCode = reverseMap.get("result_code");
if (WXPayConstants.SUCCESS.equals(returnCodeForReverse) && WXPayConstants.SUCCESS.equals(resultCode)) {
// 如果撤销成功,需要告诉客户端已经撤销订单了
responseMapForPay.put("err_code_des", "用户取消支付或尚未支付,后台已经撤销该订单,请重新支付!");
}
}
}
}
return responseMapForPay;
}
/**
* 从request的inputStream中获取参数
* @param request
* @return
* @throws Exception
*/
public Map<String, String> getNotifyParameter(HttpServletRequest request) throws Exception {
InputStream inputStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = inputStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, length);
}
outSteam.close();
inputStream.close();
// 获取微信调用我们notify_url的返回信息
String resultXml = new String(outSteam.toByteArray(), "utf-8");
Map<String, String> notifyMap = WXPayUtil.xmlToMap(resultXml);
return notifyMap;
}
/**
* 解密退款通知
*
* <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_16&index=11>退款结果通知文档</a>
* @return
* @throws Exception
*/
public Map<String, String> decodeRefundNotify(HttpServletRequest request) throws Exception {
// 从request的流中获取参数
Map<String, String> notifyMap = this.getNotifyParameter(request);
log.info(notifyMap.toString());
String reqInfo = notifyMap.get("req_info");
//(1)对加密串A做base64解码,得到加密串B
byte[] bytes = new BASE64Decoder().decodeBuffer(reqInfo);
//(2)对商户key做md5,得到32位小写key* ( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
SecretKeySpec key = new SecretKeySpec(WXPayUtil.MD5(config.getKey()).toLowerCase().getBytes(), ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
//(3)用key*对加密串B做AES-256-ECB解密(PKCS7Padding)
// java.security.InvalidKeyException: Illegal key size or default parameters
// https://www.cnblogs.com/yaks/p/5608358.html
String responseXml = new String(cipher.doFinal(bytes),"UTF-8");
Map<String, String> responseMap = WXPayUtil.xmlToMap(responseXml);
return responseMap;
}
/**
* 获取沙箱环境验签秘钥API
* <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_1">获取验签秘钥API文档</a>
* @return
* @throws Exception
*/
public Map<String, String> getSignKey() throws Exception {
Map<String, String> reqData = new HashMap<>();
reqData.put("mch_id", config.getMchID());
reqData.put("nonce_str", WXPayUtil.generateNonceStr());
String sign = WXPayUtil.generateSignature(reqData, config.getKey(), WXPayConstants.SignType.MD5);
reqData.put("sign", sign);
String responseXml = this.requestWithoutCert("https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey", reqData,
config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs());
Map<String, String> responseMap = WXPayUtil.xmlToMap(responseXml);
return responseMap;
}
}
5. WXPayConfiguration
@Configuration
@EnableConfigurationProperties(MyWXPayConfig.class)
public class WXPayConfiguration {
@Autowired
private MyWXPayConfig wxPayConfig;
/**
* useSandbox 沙盒环境
* @return
*/
@Bean
public WXPay wxPay() {
return new WXPay(wxPayConfig, WXPayConstants.SignType.MD5, wxPayConfig.getUseSandbox() );
}
@Bean
public WXPayClient wxPayClient() {
return new WXPayClient(wxPayConfig, WXPayConstants.SignType.MD5, wxPayConfig.getUseSandbox());
}
}
6. PayUtil
public class PayUtil {
/**
* 根据url生成二位图片对象
*
* @param codeUrl
* @return
* @throws WriterException
*/
public static BufferedImage getQRCodeImge(String codeUrl) throws WriterException {
Map<EncodeHintType, Object> hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET, "UTF8");
int width = 256;
BitMatrix bitMatrix = (new MultiFormatWriter()).encode(codeUrl, BarcodeFormat.QR_CODE, width, width, hints);
BufferedImage image = new BufferedImage(width, width, 1);
for(int x = 0; x < width; ++x) {
for(int y = 0; y < width; ++y) {
image.setRGB(x, y, bitMatrix.get(x, y) ? -16777216 : -1);
}
}
return image;
}
}
7. WXPayPrecreateController
/**
* 微信支付-扫码支付.
* <p>
* detailed description
*
* @author Mengday Zhang
* @version 1.0
* @since 2018/6/18
*/
@Slf4j
@RestController
@RequestMapping("/wxpay/precreate")
public class WXPayPrecreateController {
@Autowired
private WXPay wxPay;
@Autowired
private WXPayClient wxPayClient;
/**
* 扫码支付 - 统一下单
* 相当于支付不的电脑网站支付
*
* <a href="https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1">扫码支付API</a>
*/
@PostMapping("")
public void precreate(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, String> reqData = new HashMap<>();
reqData.put("out_trade_no", String.valueOf(System.nanoTime()));
reqData.put("trade_type", "NATIVE");
reqData.put("product_id", "1");
reqData.put("body", "商户下单");
// 订单总金额,单位为分
reqData.put("total_fee", "2");
// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
reqData.put("spbill_create_ip", "14.23.150.211");
// 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
reqData.put("notify_url", "http://3sbqi7.natappfree.cc/wxpay/precreate/notify");
// 自定义参数, 可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
reqData.put("device_info", "");
// 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
reqData.put("attach", "");
/**
* {
* code_url=weixin://wxpay/bizpayurl?pr=vvz4xwC,
* trade_type=NATIVE,
* return_msg=OK,
* result_code=SUCCESS,
* return_code=SUCCESS,
* prepay_id=wx18111952823301d9fa53ab8e1414642725
* }
*/
Map<String, String> responseMap = wxPay.unifiedOrder(reqData);
log.info(responseMap.toString());
String returnCode = responseMap.get("return_code");
String resultCode = responseMap.get("result_code");
if (WXPayConstants.SUCCESS.equals(returnCode) && WXPayConstants.SUCCESS.equals(resultCode)) {
String prepayId = responseMap.get("prepay_id");
String codeUrl = responseMap.get("code_url");
BufferedImage image = PayUtil.getQRCodeImge(codeUrl);
response.setContentType("image/jpeg");
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setIntHeader("Expires",-1);
ImageIO.write(image, "JPEG", response.getOutputStream());
}
}
/**
*
* @param request
* @return
* @throws Exception
*/
@RequestMapping("/notify")
public void precreateNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, String> reqData = wxPayClient.getNotifyParameter(request);
/**
* {
* transaction_id=4200000138201806180751222945,
* nonce_str=aaaf3fe4d3aa44d8b245bc6c97bda7a8,
* bank_type=CFT,
* openid=xxx,
* sign=821A5F42F5E180ED9EF3743499FBCF13,
* fee_type=CNY,
* mch_id=xxx,
* cash_fee=1,
* out_trade_no=186873223426017,
* appid=xxx,
* total_fee=1,
* trade_type=NATIVE,
* result_code=SUCCESS,
* time_end=20180618131247,
* is_subscribe=N,
* return_code=SUCCESS
* }
*/
log.info(reqData.toString());
// 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
boolean signatureValid = wxPay.isPayResultNotifySignatureValid(reqData);
if (signatureValid) {
/**
* 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
* 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,
* 判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。
* 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
*/
Map<String, String> responseMap = new HashMap<>(2);
responseMap.put("return_code", "SUCCESS");
responseMap.put("return_msg", "OK");
String responseXml = WXPayUtil.mapToXml(responseMap);
response.setContentType("text/xml");
response.getWriter().write(responseXml);
response.flushBuffer();
}
}
}
8. WXPayController
/**
* 微信支付 - 通用API.
*
* <p>
* 类似支付宝中的条码支付.
*
* @author Mengday Zhang
* @version 1.0
* @since 2018/6/15
*/
@Slf4j
@RestController
@RequestMapping("/wxpay")
public class WXPayController {
@Autowired
private WXPay wxPay;
@Autowired
private WXPayClient wxPayClient;
@Autowired
private MyWXPayConfig wxPayConfig;
/**
* 订单查询
* @param orderNo
* @return
* @throws Exception
*/
@GetMapping("/orderQuery")
public Object orderQuery(String orderNo) throws Exception {
Map<String, String> data = new HashMap<>();
data.put("out_trade_no", orderNo);
Map<String, String> result = wxPay.orderQuery(data);
return result;
}
/**
* 退款
* 注意:调用申请退款、撤销订单接口需要商户证书
* 注意:沙箱环境响应结果可能会是"沙箱支付金额不正确,请确认验收case",但是正式环境不会报这个错误
* 微信支付的最小金额是0.1元,所以在测试支付时金额必须大于0.1元,否则会提示微信支付配置错误,可以将microPay的total_fee大于1再退款
*/
@PostMapping("/refund")
public Object refund(String orderNo) throws Exception {
Map<String, String> reqData = new HashMap<>();
// 商户订单号
reqData.put("out_trade_no", orderNo);
// 授权码
reqData.put("out_refund_no", orderNo);
// 订单总金额,单位为分,只能为整数
reqData.put("total_fee", "2");
//退款金额
reqData.put("refund_fee", "2");
// 退款异步通知地址
reqData.put("notify_url", wxPayConfig.getNotifyUrl());
reqData.put("refund_fee_type", "CNY");
reqData.put("op_user_id", wxPayConfig.getMchID());
Map<String, String> resultMap = wxPay.refund(reqData);
log.info(resultMap.toString());
return resultMap;
}
/**
* 退款结果通知
*
* 特别说明:退款结果对重要的数据进行了加密,商户需要用商户秘钥进行解密后才能获得结果通知的内容
* @param request
* @throws Exception
*/
@RequestMapping("/refund/notify")
public String refundNotify(HttpServletRequest request) throws Exception {
// Map<String, String> notifyMap = new HashMap<>();
// notifyMap.put("nonce_str", "9b4e428ae262d5dca96178027e849fa9");
// notifyMap.put("req_info", "VKGj8c81RwQ45LcyWEVBE9/HsNfADGbgmbIAQZ2ydcbIFhIIcJFKFQwGfcSGgFGtQlWvg6KDNsRjmCjN+PvipJ3roynJ7cME0LOFG50VGtk4EYHqdjFzUVANI7GpT+i6Ok+ZWivH0MwoGK2fsz3WG+bYs2XJBwav/K89tKjFhZGitCKKBeGqcP99fa/gAL0swNXXNQHmL806Zi+QcACzL3E89BtP9FlXM2Gi+wPQafvPr+/iE+LrPdMlNUa5LiZnenZXUF24kMdhaTafczcKL4sZjRXQHEfEyc/pIZPbIjcNIETvHsskyzKuHVr/SAFkxaM6RR1Kl9pyWZGUjkH5SOeqsT8uL7YQmTlDXrnXmno3AvZdnepTGL5w69yIOmQNPeMqsd01ES9WX36GZYOigfi2+BJ9RRXjIffmpB/MFF+zryyvLTaJE2obCwFSHrmOD8YbaJqrZXOUvWZQrn7wIQgaCypo8V57cD3w5d2RSgIHNrdnEDYlbRcLNYgKuL+T9+1HPhU/frowZgwPN9IB53OahZV3p1Yvos23kvhqPCLn3BYgUihRbey6QhEtL2QyifiQ9e8WVLzWpRZ+DOa8VrhYvSuTfjRdjoNanqHFvXGP6uEsEa+DETqnexpB7xOS9m/CdmlNCwbdUplPEVzNQQdzYT4kybi00Y8A+EdairxfVyK9A7MAYAMtAO9yxV2ht0bn3SofFyZe/YSzdJgxdtcxBf1CVYN6x+yHcSueCSgq4cM/2VCwh4J1+pUVmNpEm0OVcdKbV5USkaxJR0h7Yd+n5FTz5Q2S/qvyDo202cUzLFPI5UqQm5X+FOrWDAkmmr5yVcDQIm3dAdb31jkz0X2TPYt5g7ciQ1h9heyVxJ67FexKvEM4pKCCubtWx6nyxcOUghHMrh8DSoBtewtNjbnwGVIbLsSb6X9MIYAkWIDbqNVP1f63GiZU+cJlhBmvcb8aeQUdTTj7EX5pOTIVSVv5D6SkKmpGU4FGvV+WjufuGX4ZRZo+01p6xl0sfZVmucG1UtxhX6bMCJb06yDwxpv7tGwkwS4TCK4SQp40Xe0=");
// notifyMap.put("appid", "xxx");
// notifyMap.put("mch_id", "xxx");
// notifyMap.put("return_code", "SUCCESS");
// 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
// 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。
// 在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
// TODO 处理业务
Map<String, String> requstInfoMap = wxPayClient.decodeRefundNotify(request);
// 商户处理退款通知参数后同步返回给微信参数
Map<String, String> responseMap = new HashMap<>();
responseMap.put("return_code", "SUCCESS");
responseMap.put("return_msg", "OK");
String responseXml = WXPayUtil.mapToXml(responseMap);
return responseXml;
}
/**
* 退款查询
* @param orderNo
* @return
* @throws Exception
*/
@GetMapping("/refundQuery")
public Object refundQuery(String orderNo) throws Exception {
Map<String, String> reqData = new HashMap<>();
reqData.put("out_trade_no", orderNo);
Map<String, String> result = wxPay.refundQuery(reqData);
return result;
}
/**
* 下载对账单
* 注意:
* 微信在次日9点启动生成前一天的对账单,建议商户10点后再获取;
* 对账单接口只能下载三个月以内的账单。
* @throws Exception
*/
@PostMapping("/downloadBill")
public Object downloadBill(String billDate) throws Exception {
Map<String, String> reqData = new HashMap<>();
reqData.put("bill_date", billDate);
reqData.put("bill_type", "ALL");
Map<String, String> resultMap = wxPay.downloadBill(reqData);
return resultMap;
}
/**
* 获取沙箱环境API秘钥,
*
* 这里只是为了获取,可以放在main方法下运行,这里作为api来运行的,实际情况下不应该暴露出来
* @return
* @throws Exception
*/
@GetMapping("/sandbox/getSignKey")
public Object getSignKey() throws Exception {
Map<String, String> signKey = wxPayClient.getSignKey();
log.info(signKey.toString());
return signKey;
}
}
获取源码
关注并私信“微信扫码支付”获取源码。
相关推荐
- 为何越来越多的编程语言使用JSON(为什么编程)
-
JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...
- 何时在数据库中使用 JSON(数据库用json格式存储)
-
在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...
- MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)
-
前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...
- JSON对象花样进阶(json格式对象)
-
一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...
- 深入理解 JSON 和 Form-data(json和formdata提交区别)
-
在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...
- JSON 语法(json 语法 priority)
-
JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...
- JSON语法详解(json的语法规则)
-
JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...
- MySQL JSON数据类型操作(mysql的json)
-
概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...
- JSON的数据模式(json数据格式示例)
-
像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...
- 前端学习——JSON格式详解(后端json格式)
-
JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...
- 什么是 JSON:详解 JSON 及其优势(什么叫json)
-
现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...
- PostgreSQL JSON 类型:处理结构化数据
-
PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...
- JavaScript:JSON、三种包装类(javascript 包)
-
JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...
- Python数据分析 只要1分钟 教你玩转JSON 全程干货
-
Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...
- 比较一下JSON与XML两种数据格式?(json和xml哪个好)
-
JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- r语言矩阵 (127)
- browsererror (114)
- exportexcel (119)
- cv2.bitwise_not (137)
- dump命令 (128)
- es6concat (126)
- heapify (127)
- java.security.egd (130)
- javax.annotation (117)
- jsstringsplit (117)
- js数字 (115)
- maven编译 (132)
- mysqlleft (128)
- nodejsbuffer (149)
- org.apache.commons.httpclient (126)
- org.jsoup (141)
- org.springframework.web (128)
- robotframework-ride (115)
- setnocounton (141)
- socket.gethostbyname (122)
- sqlmid (121)
- time.strptime (133)
- vscode格式化 (125)
- win32con (129)
- window.localstorage (126)