java "回调【 Callback 】" 的理解和使用<借鉴>

本文通过生动的例子解释了回调(callback)的概念及其实现方式,包括不同场景下如何应用回调,以及Java中实现回调的具体方法。

  对callback,暂时的理解是:A对象调用B接口的b方法,b方法又反过来调用A对象中的c方法。 

A调用B接口时把自身给B接口,至于怎么处理,由B的实现类去做,不关A的事。 


写了个例子,BadBoy,这类坏孩子喜欢打人,有个方法叫hit,hit只能对实现了Hitable的对象执行。
这时候,BadBoy已经做完了自己的事,也就是已经打完人了,然后挨打的人肯定知道是谁打了自己,


至于挨打的人是什么反应,BadBoy是无法控制的。挨打的人有可能哭有可能跑有可能报警。 


第二种理解:

 
 【1】必须有一个接口,声明实现子类必须实现的方法,比如:


    public interface Icalc{


             public Object doCalc(int a,int b);


   }


【2】中间使用类在自己方法中只针对以上接口进行编程,在自己的方法中以 接口为参数,在方法体中调用接口的方法来完成自己额业务逻辑,具体的逻辑实现不用考虑。
  具体实现被推给调用这个使用类的用户完成!


【3】最外层是真正业务逻辑代码的提供者,它去调用【2】步骤中定义的方法时,因为该方法有一个接口参数的变量,
因此它必须在这时实现这个接口,供【2】步骤中相应方法来调用。


本来是调用【2】步骤中的方法,到真正调用时,反过来【2】步骤的方法还要调用【3】步骤中对接口的实现,来完成业务逻辑,
这正应了这个概念的名字   "回调(callback)"  !


第三种理解:

 
 总听见Callback如何如何,姑且不评判它的好坏,但是的确提供了一种code的新方式。

Callback我理解是调用方 调用 被调用方函数执行过程中,
被调用方 选择执行   调用方(至少是调用方初始化出来的)的某些函数来通知   调用方或者按照调用方的意愿做某些改变。

Spring中的JdbcTemplate的query方法和execute方法就使用了大量的Callback。

初始场景:A——调用者;B——被调用者。

   最简单的方式是  A的方法调用B的过程中,A将自身this作为一个参数传递给到b的执行函数中。
    这样,在执行b的方法时,就能够反过来操纵a的方法了。但是这种方式A和B循环依赖。不是一种很好的选择。

一种更为优雅的方式是:申明一个ICallBack接口,作为执行B方法的参数。
B在执行自己代码的过程中,执行callback对象对应的方法。
那么,在A开始调用时,实现ICallBack接口(可以大量使用A自身的资源:a知道该怎么办),并且在调用B方法时将callback对象传入。
这样,A依赖于Callback,B也依赖于CallBack。因此有效的解耦。


 


应用场景:A有多个方法要调用B的某个方法,B的这个方法很多逻辑相同,但是,小部分逻辑根据A的调用方法不同而不同。
因此,使用Callback方法:1)创建ICallback接口;
  2)在A的调用地方实现callback类;
  3)在这个类中写不同的业务逻辑;
  4)B的方法写固定的业务逻辑并且接受ICallBack对象执行。


 


可能出现的问题:如果A的若干个执行方法中,要求响应对象不一致,尝试泛型是否可以解决!


实例理解:

[java]  view plain copy print ?
  1. package com.itm.CallBack;  
  2. interface Hitable {    
  3.       
  4.       
  5. /* 
  6. 挨打的人的接口,他们有一个共同的方法, 
  7. 就是beHit(BadBoy boy),既然挨打了,肯定知道是谁打的自己,所以打人者BadBoy被作为参数传进来。 
  8.  
  9.  
  10. * 
  11. */  
  12.     public void beHit(BadBoy boy);    
  13.     
  14.     public String getName();    
  15.     
  16.     public void setName(String name);    
  17. }    

[java]  view plain copy print ?
  1. package com.itm.CallBack;  
  2. class BadBoy {    
  3.     String name;    
  4.     
  5.     public BadBoy(String name) {    
  6.         this.setName(name);    
  7.     }    
  8.     
  9.     public String getName() {    
  10.         return name;    
  11.     }    
  12.     
  13.     public void setName(String name) {    
  14.         this.name = name;    
  15.     }    
  16.         //打人    
  17.     public void hit(Hitable hitable) {    
  18.         System.out.println("----------------BEGIN----------------");    
  19.         System.out.println("badboy " + this.getName() + "打了"    
  20.                 + hitable.getName() + "一拳");    
  21.           
  22.         hitable.beHit(this);  
  23.         System.out.println("-----------------END----------------");    
  24.     }    
  25.     
  26. }    

[java]  view plain copy print ?
  1. package com.itm.CallBack;  
  2. /*Child:这个类实现了Hitable,小孩挨打了,反应是哭。。*/  
  3. class Child implements Hitable {    
  4.     String name;    
  5.     
  6.     public Child(String name) {    
  7.         this.setName(name);    
  8.     }    
  9.     
  10.     public String getName() {    
  11.         return name;    
  12.     }    
  13.     
  14.     public void setName(String name) {    
  15.         this.name = name;    
  16.     }    
  17.     
  18.     @Override    
  19.     public void beHit(BadBoy boy) {    
  20.         System.out.println("child " + this.getName() + "被" + boy.getName()    
  21.                 + "打哭了");    
  22.     }    
  23.     
  24. }    

[java]  view plain copy print ?
  1. package com.itm.CallBack;  
  2. /* 
  3.  *  
  4.  * BigMan也实现了Hitable接口,这类人比较猛,挨打后的反应,是把打人者杀了。。 
  5.  *  
  6.  *  
  7.  * */  
  8. class BigMan implements Hitable {    
  9.     String name;    
  10.     
  11.     public BigMan(String name) {    
  12.         this.setName(name);    
  13.     }    
  14.     
  15.     public String getName() {    
  16.         return name;    
  17.     }    
  18.     
  19.     public void setName(String name) {    
  20.         this.name = name;    
  21.     }    
  22.     
  23.     @Override    
  24.     public void beHit(BadBoy boy) {    
  25.         System.out.println("bigman " + this.getName() + "把" + boy.getName()    
  26.                 + "杀死了");    
  27.     }    
  28.     
  29. }    

测试类:

[java]  view plain copy print ?
  1. package com.itm.CallBack;  
  2. public class CallBackTest {    
  3.     public static void main(String[] args) {    
  4.           
  5.         BadBoy badboy = new BadBoy("Tom");    
  6.         Hitable child = new Child("Cat");    
  7.         Hitable bigman = new BigMan("Boris");    
  8.         badboy.hit(child);    
  9.         badboy.hit(bigman);    
  10.     }    
  11. }    
  12.   
  13.   
  14.     


JAVA实现回调【摘抄,很经典】
熟悉MS-Windows和X Windows事件驱动设计模式的开发人员,通常是把一个方法的指针传递给事件源,当某一事件发生时来调用这个方法(也称为“回调”)。
Java的面向对象的模型目前不支持方法指针,似乎不能使用这种方便的机制。


Java支持interface,通过interface可以实现相同的回调。其诀窍就在于定义一个简单的interface,申明一个被希望回调的方法。


例如,假定当某一事件发生时会得到通知,我们可以定义一个interface:

[java]  view plain copy print ?
  1. public interface InterestingEvent {  
  2.     // 这只是一个普通的方法,可以接收参数、也可以返回值  
  3.     public void interestingEvent();  
  4. }  

这样我们就有了任何一个实现了这个接口类对象的手柄grip。


当一事件发生时,需要通知实现InterestingEvent 接口的对象,并调用interestingEvent() 方法。

[java]  view plain copy print ?
  1. class EventNotifier {  
  2.     private InterestingEvent ie;  
  3.     private boolean somethingHappened;  
  4.   
  5.     public EventNotifier(InterestingEvent event) {  
  6.         ie = event;  
  7.         somethingHappened = false;  
  8.     }  
  9.   
  10.     public void doWork() {  
  11.         if (somethingHappened) {  
  12.             // 事件发生时,通过调用接口的这个方法来通知  
  13.             ie.interestingEvent();  
  14.         }          
  15.     }  
  16. }  

在这个例子中,用somethingHappened 来标志事件是否发生。


希望接收事件通知的类必须要实现InterestingEvent 接口,而且要把自己的引用传递给事件的通知者。

[java]  view plain copy print ?
  1. public class CallMe implements InterestingEvent {  
  2.     private EventNotifier en;  
  3.   
  4.     public CallMe() {  
  5.         // 新建一个事件通知者对象,并把自己传递给它  
  6.         en = new EventNotifier(this);  
  7.     }  
  8.   
  9.     // 实现事件发生时,实际处理事件的方法  
  10.     public void interestingEvent() {  
  11.         // 这个事件发生了,进行处理  
  12.     }  
  13. }  

以上是通过一个非常简单的例子来说明Java中的回调的实现。

以下为经典实例演示:
当然,也可以在事件管理或事件通知者类中,通过注册的方式来注册多个对此事件感兴趣的对象。
1. 定义一个接口InterestingEvent ,回调方法nterestingEvent(String event) 简单接收一个String 参数。

[java]  view plain copy print ?
  1. interface InterestingEvent {  
  2.     public void interestingEvent(String event);  
  3. }  

2. 实现InterestingEvent接口,事件处理类

[java]  view plain copy print ?
  1. class CallMe implements InterestingEvent {  
  2.     private String name;  
  3.     public CallMe(String name){  
  4.         this.name = name;  
  5.     }      
  6.     public void interestingEvent(String event) {  
  7.         System.out.println(name + ":[" +event  + "] happened");  
  8.     }  
  9. }  

3. 事件管理者,或事件通知者

[java]  view plain copy print ?
  1. package com.itm.CallBack;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. class EventNotifier {  
  7.     private List<CallMe> callMes = new ArrayList<CallMe>();  
  8.       
  9.     public void regist(CallMe callMe){  
  10.         callMes.add(callMe);  
  11.     }  
  12.       
  13.     public void doWork(){  
  14.         for(CallMe callMe: callMes) {  
  15.             // 事件触发了****  
  16.             callMe.interestingEvent("sample event");  
  17.         }  
  18.     }      
  19. }  





4. 测试

[java]  view plain copy print ?
  1. public class CallMeTest {  
  2.     public static void main(String[] args) {  
  3.         EventNotifier ren = new EventNotifier();  
  4.         CallMe a = new CallMe("CallMe A");  
  5.         CallMe b = new CallMe("CallMe B");  
  6.   
  7.         // regiest  
  8.         ren.regist(a);  
  9.         ren.regist(b);  
  10.           
  11.         // test  
  12.         ren.doWork();          
  13.     }  
  14. }  
using CY_PUBLIC_LIBRARY; using CY_PUBLIC_LIBRARY.AppConfigurtaion; using CY_PUBLIC_LIBRARY.BaseModels; using CY_PUBLIC_LIBRARY.Extension; using CY_PUBLIC_LIBRARY.Middleware; using CY_PUBLIC_LIBRARY.WebRequestHelp; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Drawing.Imaging; using DoMain; using Org.BouncyCastle.Asn1.X509; using System.Diagnostics; using Newtonsoft.Json; using Model.cxbd; using Model.sys_model; using Model.hospital_model; namespace APP_API.Controllers.wechatControllers { /// <summary> /// 微信支付 /// </summary> [Route("api/[controller]")] [ApiController] [Authorize] [ApiExplorerSettings(GroupName = "base_data")] public class wechat_payController : BaseApiController { public wechat_payController(IFreeSql fsql) : base(fsql) { } /// <summary> /// 微信支付下单 /// </summary> /// <param name="payItem"></param> /// <returns></returns> [HttpPost("pay")] [Authorize] public ActionResult<retObject<payRetModel>> Pay([FromBody] payOrderModel payItem) { var app_user = _fsql.Select<app_user>(_jwt.user_info.user_id).ToOne(); var order = _fsql.Select<sys_order>(payItem.order_id).ToOne(); if (order == null) { throw new ServiceException("订单不存在"); } if (order.status != (int)wechat_refund_status.待付款) { throw new ServiceException("订单状态有误"); } order.guid = Guid.NewGuid().ToString("N"); //order.status = (int)wechat_refund_status.待接单; _fsql.Update<sys_order>(order.order_id).Set(x=>x.guid,order.guid).ExecuteAffrows(); var domain = new orderDomain(_fsql); var payfee = Convert.ToInt32(payItem.pay * 100); var order_pay = new order_pay() { amount = payItem.pay, rl = false, pay_type_id = 2, is_app = true, order_id = payItem.order_id }; var pay_id = _fsql.Insert(order_pay).ExecuteIdentity(); var name = app_user.name; if (name.Length > 40) { name = name.Substring(0, 40) + "..."; } name = name.Replace("<", "").Replace(">", ""); var nonStr = CommandHelp.GetGuid(); var PayNotifyUrl = AppConfigurtaionServices.Configuration["Wechat:PayNotifyUrl"]; var appid = AppConfigurtaionServices.Configuration["Wechat:AppId"]; var mch_id = AppConfigurtaionServices.Configuration["Wechat:MchId"]; var ipStr = AppConfigurtaionServices.Configuration["Wechat:Ip"]; var key = AppConfigurtaionServices.Configuration["Wechat:Key"]; var postData = "appid=" + appid + "&attach=" + pay_id + "&body=" + name + "&mch_id=" + mch_id + "&nonce_str=" + nonStr + "&notify_url=" + PayNotifyUrl + "&openid=" + app_user.open_id + "&out_trade_no=" + pay_id + "&spbill_create_ip=" + ipStr + "&total_fee=" + payfee + "&trade_type=JSAPI"; postData += "&key=" + key; CyLogHelp.WriteLog(postData, "微信下单"); var md5 = MD5.Create(); var bs2 = md5.ComputeHash(Encoding.UTF8.GetBytes(postData)); var sb2 = new StringBuilder(); foreach (byte bb in bs2) { sb2.Append(bb.ToString("x2")); } postData = sb2.ToString().ToUpper(); var orderXml = "<xml>" + "<appid>" + appid + "</appid>" + "<attach>" + pay_id + "</attach>" + "<body>" + name + "</body>" + "<mch_id>" + mch_id + "</mch_id>" + "<nonce_str>" + nonStr + "</nonce_str>" + "<notify_url>" + PayNotifyUrl + "</notify_url>" + "<openid>" + app_user.open_id + "</openid>" + "<out_trade_no>" + pay_id + "</out_trade_no>" + "<spbill_create_ip>" + ipStr + "</spbill_create_ip>" + "<total_fee>" + payfee + "</total_fee>" + "<trade_type>JSAPI</trade_type>" + "<sign>" + postData + "</sign>" + "</xml>"; var retStr = WebRequsetHelp.PostRequestData("https://api.mch.weixin.qq.com/pay/unifiedorder", orderXml, WebRequsetContentType.Form表单); if (retStr != "" && retStr.IndexOf("prepay_id") > 0) { XmlDocument doc = new XmlDocument(); doc.LoadXml(retStr); XmlNodeList list = doc.GetElementsByTagName("xml"); XmlNode xn = list[0]; var prepay_id = xn.SelectSingleNode("//prepay_id").InnerText; if (!string.IsNullOrEmpty(prepay_id)) { var timeSpanStr = DateTime.Now.ConvertShortDateTimeInt().ToString(); var postStr = "appId=" + appid + "&nonceStr=" + nonStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeSpanStr; postStr += "&key=" + key; var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(postStr)); var sb = new StringBuilder(); foreach (byte bb in bs) { sb.Append(bb.ToString("x2")); } var signStr = sb.ToString().ToUpper(); var ret = new payRetModel() { appId = appid, nonceStr = nonStr, package = "prepay_id=" + prepay_id, paySign = signStr, signType = "MD5", timeStamp = timeSpanStr }; return Ok(ret.GetRetObject()); } else { CyLogHelp.WriteLog(retStr + "\r\n" + orderXml, "微信下单失败"); throw new ServiceException("微信下单失败", 201); } } else { CyLogHelp.WriteLog(retStr + "\r\n" + orderXml, "微信下单失败"); throw new ServiceException("微信下单失败", 201); } } /// <summary> /// 微信支付回调 /// </summary> /// <returns></returns> [HttpPost("gspayback")] public ActionResult<string> PayNotify() { var successStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; var failStr = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[参数错误]]></return_msg></xml>"; var postStr = ""; Request.Body.Seek(0,SeekOrigin.Begin); using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) { postStr = reader.ReadToEndAsync().Result; } _fsql.Insert(new wechat_request() { content = postStr }).ExecuteAffrows(); if (!string.IsNullOrEmpty(postStr)) { XmlDocument doc = new XmlDocument(); doc.XmlResolver = null; doc.LoadXml(postStr); XmlNodeList list = doc.GetElementsByTagName("xml"); XmlNode xn = list[0]; string ret = xn.SelectSingleNode("//result_code").InnerText; string attach = xn.SelectSingleNode("//attach").InnerText; string mch_id = xn.SelectSingleNode("//mch_id").InnerText; string out_trade_no = xn.SelectSingleNode("//out_trade_no").InnerText; string transaction_id = xn.SelectSingleNode("//transaction_id").InnerText; string totalFee = xn.SelectSingleNode("//total_fee").InnerText; string openid = xn.SelectSingleNode("//openid").InnerText; decimal price = 0; if (decimal.TryParse(totalFee, out price)) { price = price / 100; } if (_fsql.Select<wechat_pay_log>().Where(x => x.transaction_id == transaction_id && x.result_code == "SUCCESS").Count() > 0) return Ok(successStr); //再去判断是否收到订单 var nonStr = CommandHelp.GetGuid(); var appid = AppConfigurtaionServices.Configuration["Wechat:AppId"]; var mymch_id = AppConfigurtaionServices.Configuration["Wechat:MchId"]; var key = AppConfigurtaionServices.Configuration["Wechat:Key"]; var postData = "appid=" + appid + "&mch_id=" + mymch_id + "&nonce_str=" + nonStr + "&out_trade_no=" + out_trade_no; postData += "&key=" + key; var md5 = MD5.Create(); var bs2 = md5.ComputeHash(Encoding.UTF8.GetBytes(postData)); var sb2 = new StringBuilder(); foreach (byte bb in bs2) { sb2.Append(bb.ToString("x2")); } postData = sb2.ToString().ToUpper(); var orderXml = "<xml>" + "<appid>" + appid + "</appid>" + "<mch_id>" + mymch_id + "</mch_id>" + "<nonce_str>" + nonStr + "</nonce_str>" + "<out_trade_no>" + out_trade_no + "</out_trade_no>" + "<sign>" + postData + "</sign>" + "</xml>"; var retStr = WebRequsetHelp.PostRequestData("https://api.mch.weixin.qq.com/pay/orderquery", orderXml, WebRequsetContentType.Form表单); if (!retStr.Contains("支付成功")) { CyLogHelp.WriteLog(retStr + "\r\n" + orderXml, "微信支付回调请求失败"); throw new ServiceException("订单未支付", 201); }; _fsql.Transaction(() => { var order = _fsql.Select<sys_order>().Where(e => e.guid == out_trade_no).ToOne(); //添加支付记录 var _paylog = new wechat_pay_log() { openid = openid, order_id = order.order_id, out_trade_no = out_trade_no, postStr = postStr, result_code = ret, total_fee = Convert.ToDecimal(totalFee), transaction_id = transaction_id, }; if (_fsql.Insert(_paylog).ExecuteAffrows() <= 0) throw new ServiceException("添加支付记录失败", 201); if (_paylog.result_code == "SUCCESS") { //支付逻辑 var domain = new orderDomain(_fsql); domain.order_pay(order.order_id); } }); return Ok(successStr); } return Ok(failStr); } /// <summary> /// 微信退款回调 /// </summary> /// <returns></returns> [HttpPost("refundnotify")] public ActionResult<string> RefundNotify() { string WechatDecrypt(string decryptStr, string key) { byte[] keyArray = Encoding.UTF8.GetBytes(key); byte[] toEncryptArray = Convert.FromBase64String(decryptStr); RijndaelManaged rDel = new RijndaelManaged(); rDel.Key = keyArray; rDel.Mode = CipherMode.ECB; rDel.Padding = PaddingMode.PKCS7; ICryptoTransform cTransform = rDel.CreateDecryptor(); byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return Encoding.UTF8.GetString(resultArray); } var successStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; var failStr = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[参数错误]]></return_msg></xml>"; var postStr = ""; Request.Body.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(Request.Body, Encoding.UTF8)) { postStr = reader.ReadToEndAsync().Result; } _fsql.Insert(new wechat_request_refund() { content = postStr }).ExecuteAffrows(); if (string.IsNullOrEmpty(postStr)) return Ok(failStr); try { XmlDocument doc = new XmlDocument(); doc.XmlResolver = null; doc.LoadXml(postStr); XmlNodeList list = doc.GetElementsByTagName("xml"); XmlNode xn = list[0]; var retcode = xn.SelectSingleNode("//return_code").InnerText; var req_info = xn.SelectSingleNode("//req_info").InnerText; if (!string.IsNullOrEmpty(req_info)) { //解密 var key = AppConfigurtaionServices.Configuration["Wechat:Key"]; var md5 = MD5.Create(); var bs2 = md5.ComputeHash(Encoding.UTF8.GetBytes(key)); var sb2 = new StringBuilder(); foreach (byte bb in bs2) { sb2.Append(bb.ToString("x2")); } var temp_key = sb2.ToString().ToLower(); var refund_info = WechatDecrypt(req_info, temp_key); if (!string.IsNullOrEmpty(refund_info)) { doc.LoadXml(refund_info); if (doc != null) { list = doc.GetElementsByTagName("root"); xn = list[0]; string transaction_id = xn.SelectSingleNode("//transaction_id").InnerText; string out_trade_no = xn.SelectSingleNode("//out_trade_no").InnerText; string refund_id = xn.SelectSingleNode("//refund_id").InnerText; string out_refund_no = xn.SelectSingleNode("//out_refund_no").InnerText; string refund_fee = xn.SelectSingleNode("//refund_fee").InnerText; string refund_status = xn.SelectSingleNode("//refund_status").InnerText; string totalFee = xn.SelectSingleNode("//total_fee").InnerText; decimal price = 0; if (decimal.TryParse(refund_fee, out price)) { price = price / 100; } if (_fsql.Select<wechat_refund_log>().Where(x => x.refund_id == refund_id && x.refund_status == "SUCCESS").Count() > 0) return Ok(successStr); _fsql.Transaction(() => { var _refundlog = new wechat_refund_log() { out_refund_no = out_refund_no, out_trade_no = out_trade_no, postStr = postStr, refund_fee = Convert.ToDecimal(price), refund_status = refund_status, refund_id = refund_id, transaction_id = transaction_id }; var _r = _fsql.Insert(_refundlog).ExecuteAffrows(); if (_r <= 0) throw new ServiceException("添加退款日志失败", 201); if (_refundlog.refund_status == "SUCCESS") { var _order = _fsql.Select<sys_order>().Where(x => x.guid == _refundlog.out_trade_no).ToOne(); if (_order == null) { throw new ServiceException("订单不存在", 201); } //找到退款记录 var refund_id = Convert.ToInt32(out_refund_no); _fsql.Update<sys_order>(refund_id).Set(e => e.status, (int)DoMain.Enum.wechat_refund_status.已取消).ExecuteAffrows(); } }); return Ok(successStr); } } } } catch { } return Ok(failStr); } } public class payRetModel { public string appId { get; set; } public string timeStamp { get; set; } public string nonceStr { get; set; } public string package { get; set; } public string signType { get; set; } public string paySign { get; set; } } public class payOrderModel { /// <summary> /// 订单ID/购物车订单id /// </summary> [Required] public int order_id { get; set; } = 0; /// <summary> /// 支付金额 /// </summary> [Required] public decimal pay { get; set; } [Required] public bool from_cart { get; set; } } } 我这个为啥状态不修改而且支付也报Object reference not set to an instance of an object.(在小程序上支付)怎么解决
最新发布
08-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值