Resolved Use HttpClient Synchronously

RobertbNZ

Active member
Joined
Jun 26, 2020
Messages
37
Programming Experience
Beginner
The advice of Skydiver for my previous question directed me to HttpClient, and I'm now successfully sending a message to my CICS web service and receiving a response with the code below. This is based on code from an HttpClient tutorial

How do I convert this so that line 22 waits for the response, throwing an error if the request times out or there are other problems.

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json;
using System.Text;

namespace MyJSv
{
    public class JSPG2Client
    {
        static readonly HttpClient client = new HttpClient();
        public async Task Post(IJSPG2 ijspg2)
        {
            try
            {
                var json = JsonConvert.SerializeObject(ijspg2);
                json = "{\"JSPG2\" : {\"IJSPG2\" : " + json + "}}";
                var data = new StringContent(json, Encoding.UTF8, "application/json");
                var url = "http://localhost:9003/cics/services/JSPG2";
                var response = await client.PostAsync(url, data);
                String result = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine(result);
            }
            catch(HttpRequestException e)
            {
                Console.WriteLine("\nException Caught!");
                Console.WriteLine("Message :{0} ", e.Message);
            }
        }
    }
}

My current tests use debugging to step through Post after it has been initiated from a button's click event (below) , but control leaves JSPG2Client.Post immediately line 22 executes.
C#:
        private void btnEnquire_Click(object sender, EventArgs e)
        {
            ijspg2.JZ_Function = "E";
            ijspg2.JZ_Employee.EMPNO = txtEmpno.Text;
            jspg2Client.Post(ijspg2);
        }
I'd really like to change the signature of the Post method to
public Post(IJSPG2 ijspg2, ref OJSPG2 ojspg2)
to simplify the interface as much as possible.

Thank you for your help,
Robert.
 
How do I convert this so that line 22 waits for the response, throwing an error if the request times out or there are other problems.
It looks like it already does that. How could you possibly be reading content from the response if it weren't?
 
It looks like it already does that. How could you possibly be reading content from the response if it weren't?
As I said, I'm currently testing using debugging step-thru. A test reaches line 22 (=1 below): -
C#:
                var response = await client.PostAsync(url, data);
                String result = response.Content.ReadAsStringAsync().Result;
When you click F10 on this line, the highlighting disappears, and the form re-appears. If you wait for a second or so the service responds, the form disappears, and line #23 is highlighted. Click F10 again and line #23 puts a value into result which can be examined: it is the JSON code that I expect from the web service.

The reason that I think that the code is running asynchronously is this behavior, plus
  • The invoking statement, jspg2Client.Post(ijspg2); (#5 in the 2nd code sample of my 1st message) produces warning message CS4014, "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call". I haven't found a way of doing this (the prompt suggestions didn't work), besides, I don't want to complicate the interface in this way.
  • I can't pass the output back as a parameter. If I attempt to write
    C#:
    public async Task Post(IJSPG2 ijspg2, ref OJSPG2 ojspg2)
    then I get message CS1988, "Async methods cannot have ref, in or out parameters"
 
Welcome to the viral nature of async/await.

You should really adopt the use of async/await to make your UI more responsive. But if you really want to get rid of the await you would do on line 21 the same thing that you did on line 22:
C#:
var response = client.PostAsync(url, data).Result;
String result = response.Content.ReadAsStringAsync().Result;
In general, this is not recommended. If that POST call takes 30 seconds to timeout, then your UI will be frozen for 30 seconds.
 
Welcome to the viral nature of async/await.

You should really adopt the use of async/await to make your UI more responsive. But if you really want to get rid of the await you would do on line 21 the same thing that you did on line 22:
C#:
var response = client.PostAsync(url, data).Result;
String result = response.Content.ReadAsStringAsync().Result;
In general, this is not recommended. If that POST call takes 30 seconds to timeout, then your UI will be frozen for 30 seconds.
I guess one option would be to use conditional compilation and use .Result in Debug and await in Release. I've never really had any issues debugging async code but I've used it mostly in web apps, so maybe the scenarios I've been stepping through have been slightly different.
 
Welcome to the viral nature of async/await.

You should really adopt the use of async/await to make your UI more responsive. But if you really want to get rid of the await you would do on line 21 the same thing that you did on line 22:
C#:
var response = client.PostAsync(url, data).Result;
String result = response.Content.ReadAsStringAsync().Result;
In general, this is not recommended. If that POST call takes 30 seconds to timeout, then your UI will be frozen for 30 seconds.
Thanks Skydiver and jmcilhinney. I'll use this suggestion for now, but I'm just starting out on the development task of creating the interface object (see diagram at the start of this query) and I'm not sure how it will all end up. I'd like to end up with everything in the object JSPG2Client, or objects that it controls, with nothing seen by the program (Windows/Web/Mobile) that uses JSPG2Client except the properties that it exposes. I'm not sure how close I can get. I'm thinking that the best solution to this might be to give JSPG2Client a property "Async" (boolean) to use either the original code or the revised synchronous version.

At the moment I'm struggling with a number of new concepts as well as a new language, so progress is slow. I keep getting thrown by the differences between C# and VB.NET, so there are going to be a lot more questions, some of them stupid. Thank you for your indulgence.

Time I went and poured myself a glass of wine I think :)
 
In the .NET world, one of the principles that everyone tries to follow (since the early days of the .NET Framework and C#) is the principle of least surprise. In general, most of the properties in C# are expected to be cheap -- e.g. they don't do a lot of work, they don't cause side effects, and should not throw any exceptions unrelated to validation in setters. Work should be done in methods rather than properties. If you are designing your class library such reading or writing to properties causes a whole lot of magic to happen in the background, you should either re-think this, or very heavily document your code and provide lots of examples and explanations of what kind of side effects should be expected.
 
In the .NET world, one of the principles that everyone tries to follow (since the early days of the .NET Framework and C#) is the principle of least surprise. In general, most of the properties in C# are expected to be cheap -- e.g. they don't do a lot of work, they don't cause side effects, and should not throw any exceptions unrelated to validation in setters. Work should be done in methods rather than properties. If you are designing your class library such reading or writing to properties causes a whole lot of magic to happen in the background, you should either re-think this, or very heavily document your code and provide lots of examples and explanations of what kind of side effects should be expected.
Thanks Skydiver, I'm bearing this in mind. The only exceptions that the interface will throw WILL be related to validation in setters, OR to a failure of some kind with the web service (as per the try/catch in the sample code that I started with). The point of this project is to make it easy for client-side developers to access the web service by:-
  • Defining what the valid field (class) names are in a way that is discoverable. If you know that the service is named "JSPG2" then you'll see that it's input message is called "IJSPG2", and the field names in IJSPG2 are ....
  • Defining what values each field (class) may contain, and providing client-side validation. Thus even if you know that the record key is EMPNO, you shouldn't need to wait for a request/response to discover that this must be numeric and in the range 1:999999.
  • Providing rules about how to access the service (GET, POST, ... what is the URL). Thus for JSPG2Client there is only POST.
  • Enforcing any process rules: for example, an update must follow an enquiry, and must return the Checksum value from the enquiry.
  • In brief, encapsulating all the rules of the web service into a single object, ensuring system consistency and making it easy to update the client-side code if the service evolves.
Currently I'm in the "Initial research" phase of this project. My test implements a few rules and I've sorted out some of my early questions.

The real project is to implement this for any JSON web service that Jazz can generate, by having Jazz generate the interfaces. When "Initial research" is more complete, can I ask you to review what I've done and my proposals for the next step?

In the meantime I think that we can mark this question as Resolved - I'll do this tomorrow when I've checked out one more thing. There'll probably be another "how to" question in a day or so about throwing an exception from a setter, but I'll try to sort it out myself first.

I really appreciate the help that you are giving me.
 
There is a recent thread here (or was it DreamInCode.net?) which was asking how to do validation in a setter.
 
Thanks Skydiver, this article will be useful in the next phase, as I turn my attention to the issue of validating the property settings. I'm marking this question "Resolved" now that I've successfully run a test with the code below, which converts a class hierarchy to JSON, sends this to the web service, gets the response, and converts this back to another class hierarchy. Yay!
C#:
    public class JSPG2Client
    {
        static readonly HttpClient client = new HttpClient();
        public ResponseJSPG2 Post(RequestJSPG2 ijspg2)
        {
            try
            {
                var json = JsonConvert.SerializeObject(ijspg2);
                var data = new StringContent(json, Encoding.UTF8, "application/json");
                var url = "http://localhost:9003/cics/services/JSPG2";
                var response = client.PostAsync(url, data).Result;
                String result = response.Content.ReadAsStringAsync().Result;
                var resultclass = JsonConvert.DeserializeObject<ResponseJSPG2>(result);
                return resultclass;
            }
            catch (HttpRequestException e)
            {
                var Er = new ResponseJSPG2();
                Er.JSPG2Response.OJSPG2.JZERROR.JZD_ERROR = "Exception! " + e.Message;
                return Er;
            }
        }
    }
In the next phase my plan is that the actual messages - RequestJSPG2 and ResponseJSPG2 - become private to JSPG2Client, and users will communicate through JSPG2Client properties. These properties will setters that validate the input against as much of the original definition as possible. "Viewstate" properties like Checksum won't be exposed.

On to the next question ! I'm starting to feel more confident with C#, I've stopped writing "Dim" when I want a new variable and I'm learning where the semicolons and { } go :)
 
Back
Top Bottom