How to POST XML using Fiddler to ASP.NET WebAPI - asp.net

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.

Related

Can't get to deserialize XML in VB.NET

I am trying, from a VBA for Excel program, to query a RESTful API, in VB.NET.
However, I can't seem to deserialize the XML properly.
On the VBA side, the code is the following:
Set objHTTP = CreateObject("MSXML2.ServerXMLHTTP")
URL = "http://localhost:50261/api/values"
objHTTP.Open "POST", URL, False
objHTTP.setRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0;Windows NT 5.0)"
objHTTP.setRequestHeader "Content-type", "application/xml"
objHTTP.send ("<?xml version=""1.0"" encoding=""UTF-8""?><KPISheet><site>mysite</site><unit>myunit</unit></KPISheet>)")
As you can see, I am sending what I believe is a well formed and very simple XML document.
On VB.NET side, I have a class, called KPISheet:
Public Class KPISheet
Public Site As String
Public Unit As String
End Class
And a WebAPI, to get the 'POST':
Public Sub PostValue(<FromBody> oKPISheet As KPISheet)
Debug.Print("toto")
End Sub
If I set a breakpoint in the Debug line, I can see that oKPISheet is Nothing, the XML file is not deserialized.
In the Output window, I get the following Error Message:
Exception thrown: 'System.Runtime.Serialization.SerializationException' in System.Runtime.Serialization.dll
I have tried with and without the tag, but I can't get it to work.
As you can see, I am sending what I believe is a well formed and very simple XML document.
Unfortunately your assumption is wrong:
<KPISheet>site>mysite</site>
That's invalid XML. You are missing an opening < for the site tag. You also seem to have some closing ) as last character in your XML.
So you can try sending valid XML:
objHTTP.send("<?xml version=""1.0"" encoding=""UTF-8""?><KPISheet><site>mysite</site><unit>myunit</unit></KPISheet>")
Also notice that the correct content type should be text/xml and not application/xml:
objHTTP.setRequestHeader "Content-type", "text/xml"
Also bear in mind that XML is case sensitive, so you should capitalize your tag names to match your property names:
objHTTP.send("<?xml version=""1.0"" encoding=""UTF-8""?><KPISheet><Site>mysite</Site><Unit>myunit</Unit></KPISheet>")
and last but not least, Web API uses the datacontract serializer by default for working with XML, so you need to include namespaces:
objHTTP.send("<?xml version=""1.0"" encoding=""UTF-8""?><KPISheet xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/WebApplication1.ViewModels.KPISheet""><Site>mysite</Site><Unit>myunit</Unit></KPISheet>")
Don't forget to adjust the namespace to match your KPISheet class.
Alternatively if you don't want to include namespaces you could switch the XML serializer being used:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
And if you want to keep using the data contract serializer you could decorate your view model with the corresponding attributes:
<DataContract(Namespace="")>
Public Class KPISheet
<DataMember>
Public Site As String
<DataMember>
Public Unit As String
End Class

ASP.NET REST service without escape characters in JSON results

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.

Windows Azure access POST data

Ok, so I can't seem to find decent Windows Azure examples. I have a simple hello world application that's based on this tutorial. I want to have custom output instead of JSON or XML. So I created my interface like:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(UriTemplate = "session/create", Method = "POST")]
string createSession();
}
public class MyService : IService
{
public string createSession()
{
// get access to POST data here: user, pass
string sessionid = Session.Create(user, pass);
return "sessionid=" + sessionid;
}
}
For the life of me, I can't seem to figure out how to access the POST data. Please help. Thanks!
If you have an HttpContext there may be a Request object that would have the form data. I'm basing part of this off the ASP.Net tag on this question, so if that is incorrect then there may be the need to handle this another way but it looks a lot like a web service to my mind.
EDIT: HttpRequest is the class that has the Form property that should be where the POST data is stored if this is an HTTP request. This is part of System.Web so it should be ready to be used pretty easily, as I recall.
Sample code showing the Request.Form property:
int loop1;
NameValueCollection coll;
//Load Form variables into NameValueCollection variable.
coll=Request.Form;
// Get names of all forms into a string array.
String[] arr1 = coll.AllKeys;
for (loop1 = 0; loop1 < arr1.Length; loop1++)
{
Response.Write("Form: " + arr1[loop1] + "<br>");
}
This presumed there was an HttpRequest instance around.
WCF Simplified Part 4: Comparing the Request/Reply and One-Way Patterns passes in a parameter so that your "createSession" method would have to take in those strings it would appear. I'm used to the ASP.Net world where there are some built-in objects like Request, Response, Server, Application and Session.
Yes, if you did try changing the method signature as there are ways to pass in parameters in that last example I linked though I don't know if that would work in your case or not.

ASP.Net JSON Web Service Post Form Data

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

Parse Accept Header

Does anyone have any suggestions (or a regular expression) for parsing the HTTP Accept header?
I am trying to do some content-type negotiation in ASP.NET MVC. There doesn't seem to be a built in way (which is fine, because there are a lot of schools of thought here), but the parsing is not entirely trivial and I would rather not re-invent the wheel if someone has already done it well and is willing to share.
Have you seen this article? It gives a pretty comprehensive implementation for parsing the Accept header and subsequently doing something useful with it.
As of .NET 4.5 (I think—Microsoft have made info on framework versions < 4.5 rather obscure these days), you can use one of the the built in parsers from System.Net.Http.Headers:
public IOrderedEnumerable<MediaTypeWithQualityHeaderValue> GetMediaTypes(string headerValue) =>
headerValue?.Split(',')
.Select(MediaTypeWithQualityHeaderValue.Parse)
.OrderByDescending(mt => mt.Quality.GetValueOrDefault(1));
Then you can do something like this:
var headerValue = "application/json, text/javascript, */*; q=0.01";
var mediaTypes = GetMediaTypes(headerValue);
Giving you a nice list of all the media types, where the preferred option is the first item. Here's a LINQPad Dump of the mediaTypes result from the example:
Hat tip to this answer, for getting me on the right track.
I've written a parser in PHP. It's not complex, but it will give you an array of mime types in order of preference.
Found another implementation in php here
After reading the xml.com article I decided to not write a function for the Accept header myself ;)
Fortunately the article points to a good library: https://code.google.com/p/mimeparse/ - in my case I need it as a Node.js module: https://github.com/kriskowal/mimeparse
Building on https://stackoverflow.com/a/49011308/275501 from https://stackoverflow.com/users/43140/mark-bell above:
public class MyController : Controller
{
[HttpGet]
[Route("/test")]
public ActionResult Index() {
// does this request accept HTML?
var acceptsHTML = IsAcceptable("text/html");
var model = FetchViewModel();
return acceptsHTML ? (ActionResult) View(model) : Ok(model);
}
private bool IsAcceptable(string mediaType) =>
Request.Headers["Accept"].Any(headerValue =>
!string.IsNullOrWhiteSpace(headerValue) &&
headerValue.Split(",").Any(segment => MediaTypeHeaderValue.Parse(segment).MediaType == mediaType));
private object FetchViewModel() {
return new { Description = "To be completed" };
}
}
The RFC is quite complex. If the regex where to follow these rules to the letter, it would become several lines long.
If you already have the Accept-header, and ignore the quotes and the parameters, you could do something like this to match each pair:
/([^()<>#,;:\\"\/[\]?={} \t]+)\/([^()<>#,;:\\"\/[\]?={} \t]+)/
* is included in the character class, so it does not need any special case in the regex.

Resources