Resolved Handling Errors in a Web Service Interface

RobertbNZ

Active member
Joined
Jun 26, 2020
Messages
37
Programming Experience
Beginner
Continuing the saga of my Web Service Interface project, I am now trying to find the best way of handling client-side errors. I think that I have almost completed the research phase, and the illustrated code all works as I intend, but before I continue with the development phase I want to check whether this is the best way of handling the situation so far.

Imagine this scenario: using MANASYS Jazz I have created a CICS (=mainframe) REST web service named MyJSv with a program JSPG2. This follows the Swagger standard making it easy to invoke with SOAPUI, Postman, or other test utilities, but for various reasons the JSON schemas lack a full description of the interface. For example in the schema Function is a string of any length, actually it must have one of the values "E", "U", "A", or "D" meaning "Enquiry", "Update", etc., and EMPNO is any text input, but actually must be an integer in the range 1 to 999999. Now YOU want to consume this service in your program, which may be a web page, windows form, mobile app, ... written in C#, VB, or whatever. To make it easy for you I have provided an interface that hides as much as possible of the complexity of JSPG2 from you, and builds in the full rules of the interface. Just as a simple dialog created the web service program MyJsv/JSPG2, a few clicks in a MANASYS Jazz dialog has created an interface for JSPG2 into YOUR project, creating a folder MyJSV into this project with various objects relating to service MyJSv and operation JSPG2.

Here is the structure of the research project, WindowsApp, so far. Imagine that everything in folder MyJSv has been generated by MANASYS Jazz, and you have written the rest (Form1). I expect that after R&D MyJSv will be a separate project within your solution, not a folder within the same project, allowing your part to be written in languages other than C#, and for the MyJSv project to be used in several different solutions.
1594934641184.png

WindowsApp has a form Form1.cs with a textbox for entering a value of EMPNO, a label for displaying messages, and a button called btnEnquiry with this code: -
C#:
        private void btnEnquire_Click(object sender, EventArgs e)
        {
            try
            {
                jspg2Client.Function = "E";
                jspg2Client.EMPNO = txtEmpno.Text;
                ojspg2 = jspg2Client.Post();
                lblMessage.Text = "Response is in ojspg2";
            }
            catch (MyJSv.JazzDataException ex)
            {
                lblMessage.Text = "Jazz Data Error, " + ex.FieldName + ":" + ex.Message;
            }
        }
With a valid EMPNO value this works, and you see the message "Response is in ojspg2" (which debugging confirms). When you enter "xxx" into txtEmpno.text the error was detected and message "Jazz Data Error, EMPNO:Value is not numeric" displayed.

JazzDataExceptions are produced from this code in 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;
        }
So far so good. But the UI would be better if the error were reported when the value of EMPNO were entered, so that you repeat the try/catch code in a txtEmpno event handler: -
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;
            }
        }
Now the error is trapped when it is easiest to correct.

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, Update, Add, Delete, New Order, Process Order ...). There are rules about when functions are valid, for example an Update 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: -

Questions.
  1. Within MyJSv I want to generate the best code possible. Can the validation code above be improved?
  2. You've discovered MyJSv / JSPG2. How do you want to find out how to use it? You'll need to know what the functions are, the rules about any sequence requirements, and the rules of the fields in their input and output messages? As developer of MANASYS Jazz there is no problem about providing Help for the function "Generate Web Service Interface", but I have to think about the tools that I need to provide to the user of this function, who generates MyJSv/JSPG2 and its interface and provides the documentation that YOU, the consumer of the web service, needs. The developer of MyJSv/JSPG2 is, in theory, somebody who works for a mainframe user (bank etc). I'd like this developer to be able to provide Help in the form that you want it, perhaps help functions for JSPG2Client, and for each of its properties and methods, not just a web page that you have to look up.
  3. Currently output data is returned with
    C#:
    ojspg2 = jspg2Client.Post();
    , but my plan is to change this to assign data to output properties of jspg2Client. This removes the requirement to return data and so it would be possible to go back to using HttpClient asynchronously, but it will remain vital that the response is received and processed before this user uses it's output or calls it again. Should I therefore reconsider making jspg2Client.post an asynchronous operation?
 
This is my personal opinion, but you are trying to force a modern programmer working in a object oriented world to write code as if they were back in the centralized mainframe days. Granted that is exactly what your product is -- a way to access a mainframe -- I feel that you should steer away from this mainframe style programming and either go with a client-server approach like a modern programmer would expect when talking to a relational database without an ORM (or even with an ORM), or if your really can't hide divide, at least make it more SOAP web services like with proxy objects and the like.

Right now, your approach does not feel like it fits in well with C#. It just feels like you are using C# just for the sake of being able to check the box to say that C# is supported. As an analogy, some small clinics and medical practices say they have complied with the US law requiring the use of electronic medical records, but the way they comply is by scanning in their old fashioned handwritten medical records... even for brand new patients who they have picked up, or any new medical encounters with current patients since the law has been enacted.
 
As you say, my product is a way to access a mainframe. As I wrote here I see the primary issue of systems development as complexity, whether you're dealing with an ancient mainframe system or a modern distributed network. I don't understand your reference to client-server and relational databases: these are of course available on mainframes as elsewhere and are used when appropriate, but they don't solve the complexity issue, they merely move it from one place in the system to another.

My current development focusses on web services as these are an excellent way to reduce complexity by encapsulating data and processing rules. A few clicks in a MANASYS dialog can generate a REST or SOAP web service to retrieve and update data, via a Jazz program that, including data definitions, may be a 20th or less of the equivalent COBOL code. However while these web services can be generated very quickly, writing the client end to invoke these web services is far from trivial. I want to make it as easy for MANASYS Jazz users to use the services that they generate as it is to generate them, which is the purpose of developing the interfaces. Like the service itself, the interface will be generated with a dialog and a few clicks, much like creating a Visual Studio project. I'm not using C# to check a box, I'm using C# because this provides the right technology to use at the client end.

At each end I want MANASYS Jazz to feel familiar and natural to its users. Thus it's not surprising that the service itself has a "mainframe feel", it needs to have this to work well and take advantage of mainframe features that support the scalability and performance that these large systems can provide. Issues and technology are different at the client end, which is why mainframe and client developers are usually separate people/teams who don't understand each other. My challenge is to bridge the gap between the two worlds: I want the tools I provide to feel right to both audiences.

My current development task is to write the first draft of a Users' Guide on how to generate (and use) an interface, this will then guide my actual development. When this is written I'll post it and ask for your comments. I'm disturbed that you think I'm trying to force client-side programmers to work as if they were using a mainframe, and that it doesn't fit well with C#, so I'd welcome any feedback on how it should differ.
 
Let me try to explain the reasoning behind my post #2. You code here:
C#:
jspg2Client.Function = "E";
jspg2Client.EMPNO = txtEmpno.Text;
ojspg2 = jspg2Client.Post();
is practically equivalent to the early days of programming where you staged all he data or inputs like on a punch card (lines 1-2), and the you sent those cards in for processing, and then you get the results back as a print out (line 3). Or if not the early days of mainframe computing, like the early days of home built computers where you set all switches of an 8 bit register, and the press the commit button, and then look at the register lights to see what the result was.

A more modern look to the same code above would look like:
C#:
ojspg2 = jspg2.Enquire(txtEmpno.Text);

Yes, the same set of operations are happening, but the complexity has been reduced by making it obvious to the programmer that to be able to do a an "enquire" operation, they would call the Enquire() method.

I understand pretty much where you are coming from in terms of programming if you respond with "but a programmer is expected to read the manual first, and look other existing code before they jump in and try to use our mainframe". My father worked in that culture and view of computing where the mainframe was the holy altar, and only the priest sysadmins where allowed to touch it directly, and other acolytes who were allowed to submit programs had to go through rigorous training and testing before they could be allowed to submit the hallowed punch cards. Although when I was learning to program, I was lucky enough to have a Pocket TRS-80, and later a C-64 where I could just pound on it, the lack of learning resources in the Philippines at that time still forced me to do a lot reading the manuals and documentation before even drawing the first flowchart, much less the first line of code. So I get that sense of "programming is complex -- you better be ready to live with it."

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()?

Anyway to answer the other part of your question about how would you force a particular order of operations, take a closer look at other SOAP APIs: Typically when you would start off by instantiating an object that has this interface:
C#:
interface IConnection
{
    ISession Logon(string username, string password);
}
and if the logon is successful, they get back an ISession that would look like:
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 Update() as part of enquiry response:
C#:
interface IEnquiryResponse
{
    IUpdateResponse Update();
    :
}
 
Thanks for this reply Skydiver, it's very helpful. I'd already made your first change: from yesterday my research project (WindowsApp) uses
C#:
ojspg2 = jspg2Client.Enquiry(txtEmpno.Text, txtWorkDept.Text, txtSkip.Text);
instead of the initial code, partly because this is an obvious simplification, and partly because it handles the alternate index on WorkDept. The rules for Employee are that you can give either value, if you give Workdept instead of Empno then there could be several qualifying records so Skip allows you to page through them. This is of course a particular situation for this particular program, other programs will have different rules, but MANASYS will know these rules and generate appropriate code for the interface.

I've been working with ASP.NET and VB.NET for > 15 years so I'm reasonably familiar with OO programming, inheritance, and the way that modern programmers think, and I've been preaching the merits of structured programming and encapsulation (although we didn't have that term) since the 1970's, it's only C# that I've been struggling with because it's very new to me. I'm also familiar with your father's environment: in 1970 I learnt to program using punched cards and PL/I on an IBM mainframe, and I know the world of user manuals of many pages and crappy indexes that, like Microsoft on-line documentation today, often tell you everything except what you want to know NOW. Like the difficulty I had trying to invoke a web service because somebody (not you) mis-directed me to Web API and MVC.

I'm trying to bring the best parts of OO programming to the mainframe world through MANASYS, while avoiding its worst bits. The best design minimizes coupling and maximizes cohesion, which implies that I put all the rules of operation (program) JSPG2 into JSPG2Client, and expose only the information that the user's client program (Form1 in WindowsApp) requires. Thus information like Function will not be seen outside JSPG2Client, instead there will be a method for each of its values. Instead of returning ojspg2 as a message, I'll be setting JSPG2Client properties, simplifying their references and omitting information (like Viewstate) that the user's client doesn't need. Update, Delete, and Add functions will all have parameter EMPNO, for Update and Delete this must be the value of the preceding Enquiry, for Add this must either be null or an unused value. I will note whether a property has been changed since the enquiry, so that Update doesn't unnecessarily set a value that hasn't changed, and there will be some readonly properties like HttpCode (hopefully usually 2xx).
 
Back
Top Bottom