解决 在Web服务界面中处理错误

罗伯特·NZ

活跃成员
已加入
2020年6月26日
留言内容
34
编程经验
Beginner
延续我的传奇 Web服务接口项目,我现在正试图找到处理客户端错误的最佳方法。我认为我已经差不多完成了研究阶段,并且所说明的代码都可以按我的预期工作,但是在继续进行开发阶段之前,我想检查一下这是否是到目前为止处理这种情况的最佳方法。

想象一下这种情况:使用 MANASYS爵士乐 我已经使用程序JSPG2创建了一个名为MyJSv的CICS(=大型机)REST Web服务。这遵循Swagger标准,可以轻松地使用SOAPUI,Postman或其他测试实用程序进行调用,但是由于各种原因,JSON模式缺乏对该接口的完整描述。例如,在架构函数中,函数是任意长度的字符串,实际上它必须具有以下值之一"E", "U", "A", or "D" meaning "Enquiry", "Update"等等,而EMPNO是任何文本输入,但实际上必须是1到999999范围内的整数。现在您想在程序中使用此服务,该程序可能是网页,Windows窗体,移动应用程序,..用C#,VB或其他任何语言编写。为了方便您,我提供了一个界面,该界面尽可能地向您隐藏JSPG2的复杂性,并构建了该界面的完整规则。就像一个简单的对话框创建了Web服务程序MyJsv / JSPG2一样,只需点击几下 MANASYS爵士乐 对话框已为您的项目中的JSPG2创建了一个接口,在该项目中创建了一个文件夹MyJSV,其中包含与服务MyJSv和操作JSPG2有关的各种对象。

到目前为止,这是研究项目WindowsApp的结构。想象一下,文件夹MyJSv中的所有内容都是由MANASYS Jazz生成的,而您已经编写了其余的内容(Form1)。我希望在R之后&D MyJSv将是您解决方案中的一个单独项目,而不是同一项目中的文件夹,从而使您的部分可以用C#以外的语言编写,并且MyJSv项目可以在多种不同的解决方案中使用。
1594934641184.png

WindowsApp的格式为Form1.cs,带有一个用于输入EMPNO值的文本框,一个用于显示消息的标签以及一个名为btnEnquiry的按钮,其代码如下:-
C#:
        private void btnEnquire_Click(object sender, EventArgs e)
        {
            try
            {
                jspg2Client.Function = "E";
                jspg2Client.EMPNO = txtEmpno.Text;
                ojspg2 = jspg2Client.Post();
                lblMessage.Text = "响应在ojspg2中";
            }
            catch (MyJSv.JazzDataException ex)
            {
                lblMessage.Text = "Jazz Data Error, " + ex.FieldName + ":" + ex.Message;
            }
        }
使用有效的EMPNO值,此方法有效,并且您会看到消息"响应在ojspg2中"(调试确认)。当您输入"xxx"进入txtEmpno.text,检测到错误并显示消息"Jazz Data Error,EMPNO:值不是数字" displayed.

JazzDataExceptions是从JSPG2Client.cs中的以下代码产生的:-
C#:
    public class JSPG2Client
    {
        ...
        // Web Service Input Properties - Visible request message
...
        private string _EMPNO = null;
        public string EMPNO  // => RequestJSPG2.JZ_Employee.EMPNO
        {
            get => _EMPNO;
            set
            {
                _EMPNO = null;
                _EMPNO = IsInt(value, 0, 999999, "EMPNO");
                ijspg2.JSPG2.IJSPG2.JZ_Employee.EMPNO = _EMPNO;
            }
        }
        //  Functions used to check input properties
...
        private string IsInt(string Value, int MinVal, int maxval, string FieldName)
        {
            int IX = 0;
            if (!int.TryParse(Value, out IX))
            {
                throw new JazzDataException("Value is Not Numeric", FieldName);
            }
            if (IX < MinVal || IX > maxval)
            {
                throw new JazzDataException("Value out of range", FieldName);
            }
            return Value;
        }
到现在为止还挺好。但是,如果输入EMPNO的值时报告了错误,则UI会更好,以便您在txtEmpno事件处理程序中重复try / catch代码:-
C#:
        private void txtEmpno_TextChanged(object sender, EventArgs e)
        {
            lblMessage.Text = "";
            try
            {
                jspg2Client.EMPNO = txtEmpno.Text;
            }
            catch (MyJSv.JazzDataException ex)
            {
                lblMessage.Text = "Jazz Data Error, " + ex.FieldName + ":" + ex.Message;
            }
        }
现在,最容易纠正的错误将被捕获。

The research project WindowsApp has dealt with a single function (Enquiry) with only a couple of fields, each with simple validation requirements. In the production situation the interface might deal with a few dozen input and output fields, some of which may have complex validation requirements like check digits and pattern validation. There may be several different types of function (Enquiry, 更新资料, Add, Delete, New Order, Process Order ...). There are rules about when functions are valid, for example an 更新资料 must follow an enquiry. There may be conversational requirements: authentication is the obvious one, but there might be requirements about a conversation sequence. WIth this in mind: -

问题。
  1. 我想在MyJSv中生成最佳代码。上面的验证代码可以改进吗?
  2. 您已经发现MyJSv / JSPG2。您想如何使用它?您需要知道什么是函数,有关任何序列要求的规则以及它们的输入和输出消息中的字段的规则?作为MANASYS Jazz的开发人员,为功能提供帮助没有问题"生成Web服务界面",但我必须考虑需要为此功能的用户提供的工具,该工具生成MyJSv / JSPG2及其接口,并提供Web服务使用者YOU所需的文档。从理论上讲,MyJSv / JSPG2的开发人员是为大型机用户(银行等)工作的人。我希望该开发人员能够以您想要的形式提供帮助,也许是JSPG2Client的帮助功能以及其每个属性和方法,而不仅仅是您必须查找的网页。
  3. 当前输出数据返回
    C#:
    ojspg2 = jspg2Client.Post();
    ,但我的计划是更改此设置以将数据分配给jspg2Client的输出属性。这消除了返回数据的要求,因此可以返回异步使用HttpClient,但是在该用户使用其输出或再次调用它之前,接收和处理响应仍然至关重要。因此,我应该重新考虑使jspg2Client.post异步操作吗?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,447
位置
弗吉尼亚州切萨皮克
编程经验
10+
这是我的个人观点,但是您正在尝试迫使在面向对象的世界中工作的现代程序员编写代码,就像它们回到集中式大型机时代一样。可以肯定的是,这正是您的产品-访问大型机的一种方式-我觉得您应该远离这种大型机风格的编程,而应该像现代程序员期望的那样使用客户端-服务器方法与关系型对象交谈没有ORM(甚至没有ORM)的数据库,或者如果您真的无法隐藏鸿沟,至少可以使其成为更多的SOAP Web服务,例如带有代理对象等。

目前,您的方法并不适合C#。感觉就像您在使用C#只是为了能够选中该框以表示支持C#。打个比方,一些小型诊所和医疗机构表示,它们已遵守要求使用电子病历的美国法律,但遵守的方式是扫描老式的手写病历……甚至对于那些自法律颁布以来,他们已经康复,或者与当前患者有任何新的医疗接触。
 

罗伯特·NZ

活跃成员
已加入
2020年6月26日
留言内容
34
编程经验
Beginner
如您所说,我的产品是访问大型机的一种方式。 正如我在这里写的 我认为系统开发的主要问题是复杂性,无论您是在处理古老的大型机系统还是现代的分布式网络。我不理解您对客户端-服务器和关系数据库的引用:它们当然可以在大型机上以及其他地方使用,并在适当的时候使用,但是它们并不能解决复杂性问题,它们只是将其从系统中的某个位置移开到另一个。

我目前的发展集中在Web服务上,因为Web服务是通过封装数据和处理规则来降低复杂性的绝佳方法。在MANASYS对话框中单击几下,便可以通过Jazz程序生成REST或SOAP Web服务来检索和更新数据,该程序包括数据定义,可能是等效COBOL代码的20或更少。但是,尽管可以非常快速地生成这些Web服务,但编写客户端来调用这些Web服务并不是一件容易的事。我想让MANASYS Jazz用户使用他们生成的服务就像生成它们一样容易,这就是开发接口的目的。与服务本身一样,该界面将通过对话框和单击几下生成,非常类似于创建Visual Studio项目。我没有使用C#来选中一个框,而是使用了C#,因为这提供了在客户端使用的正确技术。

在每一端,我都希望MANASYS Jazz对用户感到熟悉和自然。因此,服务本身具有一个"mainframe feel",它需要使其运作良好并利用大型机功能来支持这些大型系统可以提供的可伸缩性和性能。在客户端,问题和技术是不同的,这就是大型机和客户端开发人员通常是彼此不了解的独立人员/团队的原因。我的挑战是弥合两个世界之间的鸿沟:我希望我提供的工具能够让两个受众都感到满意。

我当前的开发任务是编写有关如何生成(和使用)界面的用户指南的初稿,然后以此指导我的实际开发。撰写本文时,我将其发布并征求您的意见。我感到不安的是,您认为我正在试图迫使客户端程序员像使用大型机一样工作,并且它与C#不太匹配,因此,我欢迎任何有关它应该如何与众不同的反馈。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,447
位置
弗吉尼亚州切萨皮克
编程经验
10+
让我尝试解释我的帖子2背后的原因。您在这里编码:
C#:
jspg2Client.Function = "E";
jspg2Client.EMPNO = txtEmpno.Text;
ojspg2 = jspg2Client.Post();
实际上相当于编程的早期阶段,在该阶段中,您上演了所有数据或输入,例如在打孔卡上(第1-2行),然后将这些卡发送或进行处理,然后将结果作为打印输出返回(第3行)。或者,如果不是大型机计算的早期阶段,例如,在家用计算机的早期阶段,您需要设置8位寄存器的所有开关,然后按下commit按钮,然后查看寄存器指示灯以查看结果。

上面相同代码的外观更现代:
C#:
ojspg2 = jspg2.Enquire(txtEmpno.Text);

是的,正在发生相同的一组操作,但是通过使程序员可以执行以下操作,降低了复杂性:"enquire" operation, they would call the Enquire() method.

如果您回应,我在编程方面几乎了解您的来历"但是希望程序员首先阅读本手册,并在跳入并尝试使用我们的大型机之前先查看其他现有代码。"。我父亲在那种文化和计算机领域工作,那里的大型机是圣坛,只有牧师的系统管理员才可以直接接触它,其他允许提交程序的助手必须经过严格的培训和测试,然后才能被允许提交神圣的打孔卡。尽管当我学习编程的时候,我很幸运能拥有Pocket TRS-80,后来又有了C-64,可以在上面装上它,但是当时菲律宾缺乏学习资源仍然迫使我去做在绘制第一个流程图之前,需要大量阅读手册和文档,而无需编写第一行代码。所以我有那种感觉"编程很复杂-您最好准备好使用它。"

Unfortunately, most modern programmers nowadays, will write code by the seat of their pants -- they download the enviroment/SDK and jump right in and depend on Intellisense, syntax completion, Google, and YouTube to get them to muddle to through their first programs. While still muddling through, how will they discover that they have to set the Function property prior to calling Post()?

无论如何要回答有关如何强制执行特定顺序操作的问题的另一部分,请仔细查看其他SOAP API:通常,当您从实例化具有此接口的对象开始时:
C#:
interface IConnection
{
    ISession Logon(string username, string password);
}
如果登录成功,他们将返回一个ISession,看起来像:
C#:
interface ISession
{
    IEnquiryResponse Enquire(string employeeNumber);
    IOrderResponse NewOrder();
    IAddResponse Add(...);
    IDeleteResponse Delete(...);
    :
}
and since an update can only happen as the result of an enquiry, then only expose 更新资料() as part of enquiry response:
C#:
interface IEnquiryResponse
{
    IUpdateResponse 更新资料();
    :
}
 

罗伯特·NZ

活跃成员
已加入
2020年6月26日
留言内容
34
编程经验
Beginner
谢谢Skydiver的回复,它非常有帮助。我已经进行了第一次更改:从昨天开始,我的研究项目(WindowsApp)使用
C#:
ojspg2 = jspg2Client.Enquiry(txtEmpno.Text, txtWorkDept.Text, txtSkip.Text);
而不是初始代码,部分是因为这是一个明显的简化,部分是因为它处理WorkDept上的备用索引。 Employee的规则是您可以提供任何一个值,如果您提供Workdept而不是Empno,则可能会有几条合格记录,因此Skip允许您分页浏览。对于该特定程序,这当然是特定情况,其他程序将具有不同的规则,但是MANASYS将知道这些规则并为接口生成适当的代码。

我一直在使用ASP.NET和VB.NET>15年以来,我对OO编程,继承和现代程序员的思维方式相当熟悉,并且自1970年代以来,我一直在宣讲结构化编程和封装的优点(尽管我们没有这个术语),只是我一直在努力的C#,因为它对我来说很新。我还熟悉您父亲的环境:1970年,我学会了在IBM大型机上使用打孔卡和PL / I进行编程,并且我了解许多页面和糟糕索引的用户手册,例如Microsoft在线文档今天,经常告诉您除了您现在想知道的以外的所有信息。就像我尝试调用Web服务时遇到的困难一样,因为有人(不是您)将我误定向到Web API和MVC。

我试图通过MANASYS将OO编程的最佳部分带入大型机世界,同时避免其最糟糕的情况。最佳设计可以最大程度地减少耦合并最大程度地提高内聚性,这意味着我将所有操作规则(程序)JSPG2放入JSPG2Client中,并且仅公开用户客户端程序(WindowsApp中的Form1)所需的信息。因此,在JSPG2Client外部将看不到诸如Function之类的信息,而是为其每个值提供一个方法。我将设置JSPG2Client属性,简化它们的引用并省略用户客户端不需要的信息(例如Viewstate),而不是返回ojspg2作为消息。 更新资料,Delete和Add函数都将具有参数EMPNO,对于Update和Delete,此参数必须是前面查询的值;对于Add,此参数必须为null或未使用的值。我将注意到自查询以来属性是否已更改,以便Update不必不必要地设置未更改的值,并且将有一些只读属性,例如HttpCode(希望通常为2xx)。
 
最佳 底部