none
RESTful Service in VB.NET cannot get json to work RRS feed

  • Question

  • I have created a simple RESTful service (VS.2013 Framework 4.5.1) using VB.NET and a Windows Forms client app that consumes it successfully using XML.  However, when I attempt to do a POST with a "?format=json", It fails with a sever error 400 "bad request".  I cannot figure out what I am doing wrong.

    Can anyone see from the below what I am missing in order to properly enable json? 

    Here is the Service Contract:

    <ServiceContract()>
    Public Interface IRESTService
        'The <WebInvoke... lines below inserted into the <OperationalContract... groupings are what makes the functions RESTful
        '
        'Note that the DEMO left the GET Mehods off completely, but it seems that the Method defaults to "POST"
        '   and without specifying the "GET" methods, and exception was thrown about conflicting Invokes that had duplicate "POST"s with "" as the UriTemplate.
        '   Adding ', Method:="GET"' below resolved that.
        '   I'm not sure what the implication is of requiring unique Uri/Method combinations only.

        'DELETEs
        <OperationContract()>
        <WebInvoke(UriTemplate:="", Method:="DELETE")>
        Sub DeletePerson(ByVal ID As String)

        'POSTs
        <OperationContract()>
        <WebInvoke(UriTemplate:="?format=json", Method:="POST", RequestFormat:=WebMessageFormat.Json, ResponseFormat:=WebMessageFormat.Json)>
        Function CreatePersonJson(ByVal personToCreate As Person) As Person

        <OperationContract()>
        <WebInvoke(UriTemplate:="", Method:="POST")>
        Function CreatePerson(ByVal personToCreate As Person) As Person


        'PUTs
        <OperationContract()>
        <WebInvoke(UriTemplate:="{ID}", Method:="PUT")>
        Function UpdatePerson(ByVal ID As String, ByVal personToUpdate As Person) As Person

        'GETs
        <OperationContract()>
        <WebInvoke(UriTemplate:="{ID}", Method:="GET")>
        Function GetAPerson(ByVal ID As String) As Person

        <OperationContract()>
        <WebInvoke(UriTemplate:="", Method:="GET")>
        Function GetAllPerson() As List(Of Person)


        ' TODO: Add your service operations here

    End Interface

    ' Use a data contract as illustrated in the sample below to add composite types to service operations.

    <DataContract()>
    Public Class Person

        <DataMember()>
        Public Property Age As String

        <DataMember()>
        Public Property ID As String

        <DataMember()>
        Public Property Name As String


    End Class

    Here is The Service:

    <AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)> _
    <ServiceBehavior(InstanceContextMode:=InstanceContextMode.[Single])> _
    Public Class RESTService
        Implements IRESTService

        Private persons As New List(Of Person)()
        Private personCount As Integer = 0

        Public Sub New()
        End Sub

        Public Function GetAPerson(ID As String) As Person Implements IRESTService.GetAPerson 'Note in VB that each Function must contain the specific Implements qualifier.
            Return persons.FirstOrDefault(Function(e) e.ID.Equals(ID))
        End Function

        Public Function CreatePerson(personToCreate As Person) As Person Implements IRESTService.CreatePerson, IRESTService.CreatePersonJson
            personToCreate.ID = (System.Threading.Interlocked.Increment(personCount)).ToString()
            persons.Add(personToCreate)
            Return personToCreate
        End Function

        Public Function GetAllPerson() As List(Of Person) Implements IRESTService.GetAllPerson
            Return persons.ToList()
        End Function


        Public Function UpdatePerson(ID As String, personToUpdate As Person) As Person Implements IRESTService.UpdatePerson
            Dim p As Person = persons.FirstOrDefault(Function(e) e.ID.Equals(ID))
            p.Name = personToUpdate.Name
            p.Age = personToUpdate.Age
            Return p
        End Function

        Public Sub DeletePerson(id As String) Implements IRESTService.DeletePerson
            persons.RemoveAll(Function(e) e.ID.Equals(id))
        End Sub

    End Class

    Here is the Form App Code:

    Public Class Form1

        Private Sub btnCallURI_Click(sender As Object, e As EventArgs) Handles btnCallURI.Click
            'Try
            Dim content As String
            Dim Method As String = drpMethods.SelectedItem


            Dim uri As String = Trim(txtURI.Text)

            Dim req As HttpWebRequest = TryCast(WebRequest.Create(uri), HttpWebRequest)
            req.KeepAlive = False
            req.Method = Method.ToUpper()

            If ("POST,PUT").Split(","c).Contains(Method.ToUpper()) Then
                'Console.WriteLine("Enter XML FilePath:")
                'Dim FilePath As String = Console.ReadLine()
                'content = (File.OpenText(FilePath)).ReadToEnd()

                content = txtContent.Text

                Dim buffer As Byte() = Encoding.ASCII.GetBytes(content)
                req.ContentLength = buffer.Length
                req.ContentType = "text/xml"
                Dim PostData As Stream = req.GetRequestStream()
                PostData.Write(buffer, 0, buffer.Length)
                PostData.Close()
            End If

            Dim resp As HttpWebResponse = TryCast(req.GetResponse(), HttpWebResponse)

            Dim enc As Encoding = System.Text.Encoding.GetEncoding(1252)
            Dim loResponseStream As New StreamReader(resp.GetResponseStream(), enc)

            Dim Response As String = loResponseStream.ReadToEnd()

            loResponseStream.Close()
            resp.Close()
            MsgBox(Response)
            ' Catch ex As Exception
            'MsgBox(ex.Message.ToString() & ex.StackTrace.ToString)
            'End Try


        End Sub
    End Class

    And finally the Web.config for the Service:

    <?xml version="1.0"?>
    <configuration>

      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
      </appSettings>
      <system.web>
        <compilation debug="true" strict="false" explicit="true" targetFramework="4.5.1" />
        <httpRuntime targetFramework="4.5.1"/>
      </system.web>
      <!--The system.servicemodel section default was replaced by the below for the RESTful Demo-->
      <system.serviceModel>   
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true">
        </serviceHostingEnvironment>
        <standardEndpoints>
          <webHttpEndpoint>
            <standardEndpoint name="" helpEnabled="true"
     automaticFormatSelectionEnabled="true"></standardEndpoint>
          </webHttpEndpoint>
        </standardEndpoints>
      </system.serviceModel>
      <!--The system.servicemodel section default was replaced by the above for the RESTful Demo-->
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <!--
            To browse web app root directory during debugging, set the value below to true.
            Set to false before deployment to avoid disclosing web app folder information.
          -->
        <directoryBrowse enabled="true"/>
      </system.webServer>

    </configuration>


    mp

    Wednesday, February 5, 2014 4:48 PM

Answers

  • I have resolved this issue. 

    First of all, it was only the POST that was non functional. GET's were fine.  (PUTs would not have worked for the same reason as POSTs).

    The problem was in the creating of the WebRequest on the Client side.  It needed to be done slightly differently, based on json or xml.  I introduced some logic based on the content of the uri to handle this, as below. In the bolded if-then block, you will see that for json the byte buffer had to be UTF8 encoding and content type application/json, whereas the xml encoding had to be ASCII and content type text/xml.

    Once the below changes were made, the POSTs work either way, as do the GETs, based on "json/" being in the URI or not.

    Marvin

    Public Class Form1

        Private Sub btnCallURI_Click(sender As Object, e As EventArgs) Handles btnCallURI.Click
            Try
                Dim content As String
                Dim Method As String = drpMethods.SelectedItem


                Dim uri As String = Trim(txtURI.Text)

                Dim req As HttpWebRequest = TryCast(WebRequest.Create(uri), HttpWebRequest)
                req.KeepAlive = False
                req.Method = Method.ToUpper()

                If ("POST,PUT").Split(","c).Contains(Method.ToUpper()) Then
                    'Console.WriteLine("Enter XML FilePath:")
                    'Dim FilePath As String = Console.ReadLine()
                    'content = (File.OpenText(FilePath)).ReadToEnd()

                    content = txtContent.Text
                    Dim buffer As Byte()


                    If (uri.ToUpper.Contains("JSON")) Then
                        buffer = Encoding.UTF8.GetBytes(content)
                        req.ContentLength = buffer.Length
                        req.ContentType = "application/json"
                    Else

                        buffer = Encoding.ASCII.GetBytes(content)
                        req.ContentLength = buffer.Length
                        req.ContentType = "text/xml"
                    End If

                    Dim PostData As Stream = req.GetRequestStream()
                    PostData.Write(buffer, 0, buffer.Length)
                    PostData.Close()
                End If

                Dim resp As HttpWebResponse = TryCast(req.GetResponse(), HttpWebResponse)

                Dim enc As Encoding = System.Text.Encoding.GetEncoding(1252)
                Dim loResponseStream As New StreamReader(resp.GetResponseStream(), enc)

                Dim Response As String = loResponseStream.ReadToEnd()

                loResponseStream.Close()
                resp.Close()
                MsgBox(Response)
            Catch ex As Exception
                MsgBox(ex.Message.ToString() & ex.StackTrace.ToString)
            End Try

        End Sub

    End Class


    mp

    Wednesday, February 5, 2014 8:03 PM

All replies

  • I have resolved this issue. 

    First of all, it was only the POST that was non functional. GET's were fine.  (PUTs would not have worked for the same reason as POSTs).

    The problem was in the creating of the WebRequest on the Client side.  It needed to be done slightly differently, based on json or xml.  I introduced some logic based on the content of the uri to handle this, as below. In the bolded if-then block, you will see that for json the byte buffer had to be UTF8 encoding and content type application/json, whereas the xml encoding had to be ASCII and content type text/xml.

    Once the below changes were made, the POSTs work either way, as do the GETs, based on "json/" being in the URI or not.

    Marvin

    Public Class Form1

        Private Sub btnCallURI_Click(sender As Object, e As EventArgs) Handles btnCallURI.Click
            Try
                Dim content As String
                Dim Method As String = drpMethods.SelectedItem


                Dim uri As String = Trim(txtURI.Text)

                Dim req As HttpWebRequest = TryCast(WebRequest.Create(uri), HttpWebRequest)
                req.KeepAlive = False
                req.Method = Method.ToUpper()

                If ("POST,PUT").Split(","c).Contains(Method.ToUpper()) Then
                    'Console.WriteLine("Enter XML FilePath:")
                    'Dim FilePath As String = Console.ReadLine()
                    'content = (File.OpenText(FilePath)).ReadToEnd()

                    content = txtContent.Text
                    Dim buffer As Byte()


                    If (uri.ToUpper.Contains("JSON")) Then
                        buffer = Encoding.UTF8.GetBytes(content)
                        req.ContentLength = buffer.Length
                        req.ContentType = "application/json"
                    Else

                        buffer = Encoding.ASCII.GetBytes(content)
                        req.ContentLength = buffer.Length
                        req.ContentType = "text/xml"
                    End If

                    Dim PostData As Stream = req.GetRequestStream()
                    PostData.Write(buffer, 0, buffer.Length)
                    PostData.Close()
                End If

                Dim resp As HttpWebResponse = TryCast(req.GetResponse(), HttpWebResponse)

                Dim enc As Encoding = System.Text.Encoding.GetEncoding(1252)
                Dim loResponseStream As New StreamReader(resp.GetResponseStream(), enc)

                Dim Response As String = loResponseStream.ReadToEnd()

                loResponseStream.Close()
                resp.Close()
                MsgBox(Response)
            Catch ex As Exception
                MsgBox(ex.Message.ToString() & ex.StackTrace.ToString)
            End Try

        End Sub

    End Class


    mp

    Wednesday, February 5, 2014 8:03 PM
  • Hi,

    I am very glad that you have solved your problem by yourself and thank you very much for sharing the solution to us.

    If you have any other problem, welcome to post it in the wcf forum.

    Best Regards,
    Amy Peng


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.


    Thursday, February 6, 2014 2:57 AM
    Moderator