I'm calling a REST service searchFavoriteCompany that in turn calls another service A.
Service A already returns JSON. But since my return type of searchFavoriteCompany my search results are then returned with escape characters:
"{\"responseHeader\":{\"status\":0,\"QTime\":2,\"params\":{\"facet\":\"false\",\"fl\":\"id,title,friendlyurl,avatar,locpath,objectid,objecttype\",\"indent\":\"off\",\"start\":\"0\",\"q\":\"title_search:*castle*\",\"wt\":\"json\",\"fq\":\"userid:\\\"C325D42C-A777-4275-BDD2-D7810A8AB9AB\\\"\",\"rows\":\"10\",\"defType\":\"lucene\"}},\"response\":{\"numFound\":2,\"start\":0,\"docs\":[{\"title\":\"Castle A\",\"objecttype\":1,\"friendlyurl\":\"castle-a\",\"avatar\":\"6_887_castle-a.JPG\",\"objectid\":6},{\"title\":\"Castle B\",\"objecttype\":1,\"friendlyurl\":\"castle-b\",\"avatar\":\"794_360_13j-Castle-by-night.jpg\",\"objectid\":794}]}}\u000a"
I don't know how to make sure my JSON results are returned without these escape characters.
Imyservice.vb
Namespace RestService
<ServiceContract()>
Public Interface Imyservice
<OperationContract()> _
_
Function searchFavoriteCompany(ByVal q As String, ByVal uuid As String) As String
End Interface
End Namespace
iservice.svc.vb
Namespace RestService
Public Class iservice
Implements Imyservice
Public Function searchFavoriteCompany(ByVal q As String, ByVal uuid As String) As String Implements Imyservice.searchFavoriteCompany
Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost/getjson") <---- this service already returns JSON data
Dim Resp As HttpWebResponse = req.GetResponse()
Dim reader As StreamReader = New StreamReader(Resp.GetResponseStream)
Dim responseString As String = reader.ReadToEnd()
Return responseString
End Function
End Class
End Namespace
Instead of returning the responsestring I also tried:
HttpContext.Current.Response.Write(responseString)
return ""
But then I get the error 'Object reference not set to an instance of an object'
And I checked: responsestring contains a value, so that is not the problem.
If you can't pass the output directly to the response, your best bet is to go back to traditional methods, and return a type-safe object from your method. You've got the JSON that the original service passes you, and your end-goal is to return that exact object back to your caller.
Assuming you've got a complete JSON object in your example - you can first go to json2csharp to take your JSON and create classes out of it. It uses C#, but you can easily convert it to VB using something like Telerik's Code Converter.
If you follow these steps, you'll end up with several classes, including one called RootObject, which contains properties for responseHeader and response, and the other classes needed. You can rename the classes as you see fit, and put them into proper namespaces - just leave the property names alone, so they match the JSON.
Once you've got this RootObject class (or whatever you rename it to), you can deserialize the JSON you got from the original service into an object of type RootObject, then your WebAPI method would simply return a RootObject. Just like any other WCF call, the framework will take care of serializing that object back into JSON (or XML) for the caller.
Related
I have an asp.net website which has an added webservice reference. Everything worked until recently when I started migrating some of the functions into their own classes. Everything works if I keep all the code inside the page.
When I make a Public Function X inside my functions class which returns a response object type of the webservice I get an error in the page calling this function:
Value of type 'wsRef.ResponseObject' cannot be converted to 'wsRef.ResponseObject'
Webservice:
Referenced as wsRef
Page:
Dim myFunctionsClass As new csFunctions
Dim resObj As New wsRef.ResponseObject
resObj = myFunctionsClass.callWS("ABC1234")
Class csFunctions:
Public Function callWS(ByVal param As String) As wsRef.ResponseObject
'/ call ws etc, get result back and return it
Return wsResObject
End Function
I don't understand why this exception, objects are clearly of the same type, they are both specified from the same namespace (wsRef).
Given the following ASP.NET WebAPI, I am trying to send a test POST using Fiddler, but can't get it to work. Whatever I send, I always just see the No data sent to service message.
Imports System.Web.Http
Imports System.Net.Http
Imports System.Net
Namespace HelloWebApiDemo
Public Class MyApiController
Inherits ApiController
Public Function [Get]() As HttpResponseMessage
Return Request.CreateResponse(HttpStatusCode.OK, "Hello")
End Function
Public Class MyXmlData
Public Property UserName As String
Public Property Password As String
Public Property SomeData As String
End Class
Public Function Post(<FromBody> x As MyXmlData) As HttpResponseMessage
If x Is Nothing Then
Return Request.CreateResponse(HttpStatusCode.InternalServerError, "No data sent to service")
End If
Dim u As String = String.Empty
Dim p As String = String.Empty
Dim d As String = String.Empty
If Not String.IsNullOrEmpty(x.UserName) Then
u = x.UserName
End If
If Not String.IsNullOrEmpty(x.Password) Then
p = x.Password
End If
If Not String.IsNullOrEmpty(x.SomeData) Then
d = x.SomeData
End If
Return Request.CreateResponse(HttpStatusCode.OK, String.Format("You posted {0}, {1} with a string that is {2} characters in length", u, p, d.Length.ToString))
End Function
End Class
End Namespace
In Fiddler, my POST looks like this:
Can anyone please advise on what I'm doing wrong? I used Content-Type: text/xml hoping that ASP.NET would be able to decipher it correctly.
Update
New screen grab following input:
Keep the Content-Type: text/xml request header and change the XML to like this.
<MyApiController.MyXmlData
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/HelloWebApiDemo.HelloWebApiDemo">
<Password>somepassword</Password>
<SomeData>somedata</SomeData>
<UserName>bob</UserName>
</MyApiController.MyXmlData>
Final XML snippet that was successfully deserialized:
<MyApiController.MyXmlData
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/_WebApplication1.HelloWebApiDemo">
<Password>Password</Password>
<SomeData>Data here</SomeData>
<UserName>Some Username</UserName>
</MyApiController.MyXmlData>
_WebApplication1 is the name of the solution. That must've been what was missing.
For those who have the ability to do this when faced with the same issue, another option is to use JSON. I am a huge fan of XML, but unfortunately, it seems things are far more stacked against XML's usage in scenarios like this, because in this case, the standard WebApi parser requires all that hideous extra stuff, including intimate knowledge of backend namespaces and types, as well as a full XML spec namespace (* see rant below).
{
"SomeData": "R2D2",
"UserName": "Johny",
"Password": "password",
"Num": 1013,
"IsCool": true
}
*( Rant, please ignore if you want to: Why require this, oh ye good gentleman who made WepApi and other such frameworks? why not allow the XML to be liberated, freely receivable without all this 'ugly'? If included, it could still specify something useful, if one needs to, but why must the rest of us include this ugliness? It seems things have been (arbitrarily, though perhaps more for historical baggage reasons, sorry you poor XML creature, you're stuck with yesterday's baggage though it need not be) stacked against XML's usage, including that, even with all this, a standard parser likely would not allow you to post the XML with the values as XML attributes. )
You can mark your entity class with DataContract attribute setting namespace to an empty string:
<DataContract(Namespace := "")>
Public Class MyXmlData
End Class
After that xmlns parameters will not be necessary.
I'm using a structure as follows:
Structure USERREC
Public UserRecID As Integer
Public CompanyRecID As Integer
Public Username As String
Public LoggedInTime As DateTime
Public UserIsLocked As Boolean
Public UserFirstName As String
Public UserLastName As String
Public UserEmail As String
... deleted for simplicity ...
End Structure
When I instantiate this type into a variable (Dim ThisUserRec As USERREC = (data routine) ), I can break the code and go to the immediate window and simply type "? ThisUserRec" and I see a nice display of every element in the structure
? ThisuserRec
{USERREC}
UserFirstName: "Alex "
UserIsDeleted: False
UserIsLocked: False
UserIsOwnerMgr: False
I removed elements for security and simplicity purposes in this example.
So, I'd love to save the entire structure into my audit table as a string. I was hoping there was some easy way like
Dim ThisUserRecString as String = ThisUserRec
but of course, I know that won't work.
Anybody know a quick way to dump the contents of a structure into a string? Or an HTML table?
You can easely serialize to JSon with DataContractJsonSerializer.
This guy has a great example: How can I make DataContractJsonSerializer serialize an object as a string?
I converted it to VB
Private Function ToJson(Of T)(data As T) As String
Dim serializer As New DataContractJsonSerializer(GetType(T))
Using ms As New MemoryStream()
serializer.WriteObject(ms, data)
Return Encoding.[Default].GetString(ms.ToArray())
End Using
End Function
Just call it like this
ToJson(Of USERREC)(object)
Don't forget to import the libraries
Imports System.Runtime.Serialization.Json
Imports System.IO
Imports System.Text
And make it serializable
<Serializable()> _
In the following I am trying to define a Private variable on Class level called _p. The HTTP.POST for Index will bring a User provided value which I'll set this private variable with. In the second Method called ListOfVehicles, I'll be accessing this variable.
Now everything is alright theoretically, however when I try to access this private variable I don't get anything, this is found Nothing.
Public Class QuotationController
Inherits System.Web.Mvc.Controller
'Private Variables
Dim _p As String
'Get Basic pickup and dropoff details
Function Index() As ActionResult
Return View()
End Function
'Function to get basic details out of the view
'and to redirect to ListOfVehicles
<HttpPost()>
Function Index(ByVal P As String, ByVal D As String) As ActionResult
_p = P
Return RedirectToAction("ListOfVehicles")
End Function
'Show list of vehicels
Function ListofVehicles() As ActionResult
ViewData("UserChoice") = "Pickup: " & _p
vehicleList = QB.GetQuotation(_p, _d)
Return View(vehicleList)
End Function
End Class
That is fundamentally impossible.
Each HTTP request gets a separate controller instance; they don't share anything.
You should use cookies, session, application state, or cache, as appropriate.
In your case, you should probably include that variable in a POST to the other action from a <form>.
If you don't want to add a formal post parameter you can use
TempData.Add("P", P);
just before the return statement, in your ListOfVeicles you can accesso via
string p = TempData["P"];
Temp data is valid just within the request scope
EDIT: sorry for C# syntax, I'm not using VB since the good old days ov VB 6
I have a ASP.NET web service decorated with System.Web.Script.Services.ScriptService() so it can return json formatted data. This much is working for me, but ASP.Net has a requirement that parameters to the web service must be in json in order to get json out.
I'm using jquery to run my ajax calls and there doesn't seem to be an easy way to create a nice javascript object from the form elements. I have looked at serialiseArray in the json2 library but it doesn't encode the field names as property name in the object.
If you have 2 form elements like this
<input type="text" name="namefirst" id="namefirst" value="John"/>
<input type="text" name="namelast" id="namelast" value="Doe"/>
calling $("form").serialize() will get you the standard query string
namefirst=John&namelast=Doe
calling JSON.stringify($("form").serializeArray()) will get you the (bulky) json representation
[{"name":"namefirst","value":"John"},{"name":"namelast","value":"Doe"}]
This will work when passing to the web service but its ugly as you have to have code like this to read it in:
Public Class NameValuePair
Public name As String
Public value As String
End Class
<WebMethod()> _
Public Function GetQuote(ByVal nvp As NameValuePair()) As String
End Function
You would also have to wrap that json text inside another object nameed nvp to make the web service happy. Then its more work as all you have is an array of NameValuePair when you want an associative array.
I might be kidding myself but i imagined something more elegant when i started this project - more like this
Public Class Person
Public namefirst As String
Public namelast As String
End Class
which would require the json to look something like this:
{"namefirst":"John","namelast":"Doe"}
Is there an easy way to do this? Obviously it is simple for a form with two parameters but when you have a very large form concatenating strings gets ugly. Having nested objects would also complicate things
The cludge I have settled on for the moment is to use the standard name value pair format stuffed inside a json object. This is compact and fast
{"q":"namefirst=John&namelast=Doe"}
then have a web method like this on the server that parses the query string into an associate array.
<WebMethod()> _
Public Function AjaxForm(ByVal q As String) as string
Dim params As NameValueCollection = HttpUtility.ParseQueryString(q)
'do stuff
return "Hello"
End Sub
As far a cludges go this one seems reasonably elegant in terms of amount of code, but my question is: is there a better way? Is there a generally accepted way of passing form data to asp.net web/script services?
You are just having a formatting crisis.
To properly call this code:
Public Class NameValuePair
Public name As String
Public value As String
End Class
<WebMethod()> _
Public Function GetQuote(ByVal nvp As NameValuePair()) As String
End Function
You need to send a json string that looks like this:
'{"nvp": {"name": "john", "value": "foo"}}'
Do not use jQuery to serialize ScriptService arguments. Use the standard json2.js.
Try this:
Form
...
<input type="text" name="name" id="name" value="John"/>
<input type="text" name="value" id="value" value="Foo"/>
...
Script:
var myNvp = {name: $('#name').val(), value:$('#value').val()};
var data = JSON.stringify({nvp: myNvp});
// data is what you post to the service.
I just wrote this off the top of my head but it looks right to me.
Let me know if you have any other questions.
I havn't found anything better than what i was already thinking with passing the serialized string as a parameter.
input:
{"q":"namefirst=John&namelast=Doe"}
webservice:
<WebMethod()> _
Public Function AjaxForm(ByVal q As String) as string
Dim params As NameValueCollection = HttpUtility.ParseQueryString(q)
'do stuff
return "Hello"
End Sub
This seems the cleanest and simplest option