MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

Tuesday, June 17, 2025

Okay, was looking back through WCF earlier this week for a prospective client, and figured I'd leave some notes.

Here's the stock code, give or take:

public interface IService
{
    [OperationContract]
    string GetData(int value);
    // ...
}
public class Service : IService
{
    public string GetData(int value)
    {
    	return string.Format("You entered: {0}", value);
    }

    // ...
}

And all that's in Service.svc is <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

Again, this is the stock WCF project so far. Not overly fancy.

I wanted to be able to hit the stock endpoint from a WCF project in Visual Studio without using a WCF client. Getting to the WSDL file was easy: You can steal that from the page the WCF Test Client refers to if nothing else.

For me, that stock page was:

http://localhost:60817/Service.svc

Here's some of the startup page's contents:

You have created a service.

To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:

svcutil.exe http://localhost:60817/Service.svc?wsdl

You can also access the service description as a single file:

http://localhost:60817/Service.svc?singleWsdl

Then the WSDL itself:

http://localhost:60817/Service.svc?wsdl

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="Service" targetNamespace="http://tempuri.org/"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"
	xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
	xmlns:wsa10="http://www.w3.org/2005/08/addressing"
	xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
	xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
	xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
	xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
	xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
	xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:tns="http://tempuri.org/"
	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
	xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
	<wsdl:types>
		<xsd:schema targetNamespace="http://tempuri.org/Imports">
			<xsd:import schemaLocation="http://localhost:60817/Service.svc?xsd=xsd0" namespace="http://tempuri.org/"/>
			<xsd:import schemaLocation="http://localhost:60817/Service.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
			<xsd:import schemaLocation="http://localhost:60817/Service.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/"/>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="IService_GetData_InputMessage">
		<wsdl:part name="parameters" element="tns:GetData"/>
	</wsdl:message>
	<wsdl:message name="IService_GetData_OutputMessage">
		<wsdl:part name="parameters" element="tns:GetDataResponse"/>
	</wsdl:message>
	<wsdl:message name="IService_GetDataUsingDataContract_InputMessage">
		<wsdl:part name="parameters" element="tns:GetDataUsingDataContract"/>
	</wsdl:message>
	<wsdl:message name="IService_GetDataUsingDataContract_OutputMessage">
		<wsdl:part name="parameters" element="tns:GetDataUsingDataContractResponse"/>
	</wsdl:message>
	<wsdl:portType name="IService">
		<wsdl:operation name="GetData">
			<wsdl:input wsaw:Action="http://tempuri.org/IService/GetData" message="tns:IService_GetData_InputMessage"/>
			<wsdl:output wsaw:Action="http://tempuri.org/IService/GetDataResponse" message="tns:IService_GetData_OutputMessage"/>
		</wsdl:operation>
		<wsdl:operation name="GetDataUsingDataContract">
			<wsdl:input wsaw:Action="http://tempuri.org/IService/GetDataUsingDataContract" message="tns:IService_GetDataUsingDataContract_InputMessage"/>
			<wsdl:output wsaw:Action="http://tempuri.org/IService/GetDataUsingDataContractResponse" message="tns:IService_GetDataUsingDataContract_OutputMessage"/>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="BasicHttpBinding_IService" type="tns:IService">
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
		<wsdl:operation name="GetData">
<!-- ================================================================================= -->
<!-- This part is reasonably important later.                                                     -->
<!-- ================================================================================= -->
<soap:operation soapAction="http://tempuri.org/IService/GetData" style="document"/>
<!-- ================================================================================= -->
<!-- ================================================================================= -->
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="GetDataUsingDataContract">
			<soap:operation soapAction="http://tempuri.org/IService/GetDataUsingDataContract" style="document"/>
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="Service">
		<wsdl:port name="BasicHttpBinding_IService" binding="tns:BasicHttpBinding_IService">
			<soap:address location="http://localhost:60817/Service.svc"/>
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>

What you want to be able to do to invoke a service endpoint directly is, of course, find its URL. In my case, with a little help from SoapUI, I figured out it was the same URL that started up initially:

http://localhost:60817/Service.svc

The first key is that you have to set up two headers to the request:

  • Content-Type
    • In my case, text/xml
  • SOAPAction
    • So for me, based on the WSDL above, that's http://tempuri.org/IService/GetData for the GetData action.

But you seem to have to send it a body in a POST -- GET gives you that stock opening page again. (Since GET and POST are REST conventions, I half-way expected the WCF service not to care, especially since we know GET can have a body now.)

I am not yet going to claim I know how the WSDL tells you what your input parameters should have looked like.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:GetData>
         <!--Optional:-->
         <tem:value>5</tem:value>
      </tem:GetData>
   </soapenv:Body>
</soapenv:Envelope>

Again, do this all in a Postman POST request with a body that's raw with XML as its type and you're golden. Change tem:value to another int to see more insanely interesting messages like...

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <GetDataResponse xmlns="http://tempuri.org/">
            <GetDataResult>You entered: 5</GetDataResult>
        </GetDataResponse>
    </s:Body>
</s:Envelope>

I do not miss XML.


Update: Ha! Wish I'd talked to myself from 13 years ago first!

Labels: , ,


posted by Jalindrine at 6/17/2025 07:27:00 PM
Monday, June 16, 2025

Looking into updating a slew of VB.NET API to C# for a client, and figured I should brush off my VB.NET before getting too serious.

And I figured that means I should make a console app, connect to a sample API URI at JsonPlaceholder, grab a collection of entities, serialize them, loop through, and access something on each individual entity instance. That's half (or more) of what any API does, right?

Oh wow. Slightly larger rabbit hole than I figured.

First, you can't make the top-level Main sub Async in VB.NET. DotNetPerls makes it sound like you can wait out an Async Sub from the Sub Main, but you can't.

From stackoverflow.com:

Subs should not be async. Event handlers are the only exception to that rule. You await Task which can only be returned from a Function. If the intention is to make that interface async then all the members need to be functions that return a Task or its derivative.

Can confirm.

The right answer is to set up an awaiter in a synchronous [sic] Sub Main.

From stackoverflow.com:

You won't be able to do top-level Await in a console program. You can still make it work (preserving the Async on LoadData) with the following changes:

  1. Change the signature of LoadData to Private Async Function LoadData() As Task
  2. Change the call in Main to LoadData.GetAwaiter().GetResult()

It is somewhat amazing how few upvotes these have gotten at SO. Aka "VB.NET was never super popular, relatively speaking [to C#], was it?"

Here's some working code.

Imports Newtonsoft.Json

Module Program

    Public Sub Main()
        Console.WriteLine("Hello World!")

        DoStuff.GetAwaiter().GetResult()

        Console.WriteLine("Done")
    End Sub

    Private Async Function DoStuff() As Task
        Dim httpClient As New Net.Http.HttpClient
        httpClient.BaseAddress = New Uri("https://jsonplaceholder.typicode.com/")

        Try
            Console.WriteLine("Starting")
            ' BaseAddress is strangely draconian:
            ' https://stackoverflow.com/a/23438417/1028230
            'Dim result1 As String = Await webClient.GetStringAsync("https://jsonplaceholder.typicode.com/users/1/todos") ' if you don't use BaseAddress
            Dim result1 As String = Await httpClient.GetStringAsync("users/1/todos")
            Console.WriteLine("Ending")

            Dim allTodos = JsonConvert.DeserializeObject(Of IEnumerable(Of Todo))(result1)

            For Each element In allTodos
                Console.Write(element.Id & "--")
            Next
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Function
End Module

and the Todo class looks like this:

Public Class Todo
    '{
    '  "userId": 1,
    '  "id": 1,
    '  "title": "delectus aut autem",
    '  "completed": false
    '},
    Public Property UserId As Integer
    Public Property Id As Integer
    Public Property Title As String
    Public Property Completed As Boolean
End Class

As I think I've mentioned before, a prof told me in college, "They're all different dialects of the same language." Or as a buddy on the ultimate team said, "It's all zeroes and ones." Nothing fancy going on here, but it does take a while to get your ears tuned to the new (ancient?) dialect.

Labels: ,


posted by Jalindrine at 6/16/2025 11:24:00 AM

<< Older | Newer >>


MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Support freedom
All posts can be accessed here:


Just the last year o' posts:
  • 06/01/2025 - 07/01/2025
  • 05/01/2025 - 06/01/2025
  • 04/01/2025 - 05/01/2025
  • 01/01/2025 - 02/01/2025
  • 12/01/2024 - 01/01/2025
  • 10/01/2024 - 11/01/2024
  • 08/01/2024 - 09/01/2024
  • 07/01/2024 - 08/01/2024
  • 06/01/2024 - 07/01/2024
  • 05/01/2024 - 06/01/2024
  • 04/01/2024 - 05/01/2024
  • 03/01/2024 - 04/01/2024

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.