Calling MVC HttpPost method (with Parameter) using HttpwebRequest - c#

I have the following MVC method.
[System.Web.Mvc.HttpPost]
public ActionResult Listen(string status)
{
CFStatusMessage statusMessage = new CFStatusMessage();
if (!string.IsNullOrEmpty(status))
{
statusMessage = Newtonsoft.Json.JsonConvert.DeserializeObject<CFStatusMessage>(status);
}
return Content(Server.HtmlEncode(status));// View(statusMessage);
}
I am trying to call the above method from Other application .. (Console). I am using HttpWebRequest to make a call to the MVC Method. Using the below code its able to call the method but the Parameter is always coming as empty string.
string content = "{\"status\":\"success\",\"payload\":\"some information\"}";
string url = "http://myrl.com";
var httpWRequest = (HttpWebRequest) WebRequest.Create(url);
httpWRequest.Method = "POST";
httpWRequest.ContentType = "text/json";
var encoding = new ASCIIEncoding();
byte[] data = encoding.GetBytes(string.Format("status={0}", Uri.EscapeDataString(content)));
httpWRequest.ContentLength = data.Length;
Stream stream = httpWRequest.GetRequestStream();
stream.Write(data, 0, data.Length);
var response = (HttpWebResponse)httpWRequest.GetResponse();
With this its making a call to Listen method but status parameter is always coming blank. whereas I want the json string {status:"success",payload:"some information"} as parameter.
What am I doing wrong?
P.S.: I tried the below statement as well, while sending the actual content.
byte[] data = encoding.GetBytes(content);
Regards,
M

If do you need to provide any kind of service from MVC tryout WebApi instead. You can use HTTP REST to do this easily.
Read more here ASP.NET WebApi

You appear to be saying the request is json, but sending it using wwwencoding.
Remove the status={0} line bit & just send the json as is.

You can try something like this
using (var sw = new StreamWriter(httpWRequest.GetRequestStream()))
{
sw.Write(content);
sw.Flush();
sw.Close();
}

Related

C# Web API REST Service POST

I originally asked a question regarding a WCF web service that I was trying to write and then found that the ASP.net web API was more appropriate to my needs, due to some feedback on here.
I've now found a good tutorial that tells me how to create a simple REST service using Web API which works well pretty much out of the box.
My question
I have a POST method in my REST service server:
// POST api/values/5
public string Post([FromBody]string value)
{
return "Putting value: " + value;
}
I can POST to this using POSTER and also my C# client code.
However the bit I don't understand is why I have to prepend an '=' sign to the POST data so that it reads: "=Here is my data which is actually a JSON string"; rather than just sending: "Here is my data which is actually a JSON string";
My C# Client that talks to the REST service is written as follows:
public string SendPOSTRequest(string sFunction, string sData)
{
string sResponse = string.Empty;
// Create the request string using the data provided
Uri uriRequest = GetFormRequest(m_sWebServiceURL, sFunction, string.Empty);
// Data to post
string sPostData = "=" + sData;
// The Http Request obj
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriRequest);
request.Method = m_VERB_POST;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(sPostData);
request.ContentLength = byteArray.Length;
request.ContentType = m_APPLICATION_FORM_URLENCODED;
try
{
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
sResponse = reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
//Log exception
}
return sResponse;
}
private static Uri GetFormRequest(string sURL, string sFunction, string sParam)
{
StringBuilder sbRequest = new StringBuilder();
sbRequest.Append(sURL);
if ((!sURL.EndsWith("/") &&
(!string.IsNullOrEmpty(sFunction))))
{
sbRequest.Append("/");
}
sbRequest.Append(sFunction);
if ((!sFunction.EndsWith("/") &&
(!string.IsNullOrEmpty(sParam))))
{
sbRequest.Append("/");
}
sbRequest.Append(sParam);
return new Uri(sbRequest.ToString());
}
Is anybody able to explain why I have to prepend the '=' sign as in the above code (string sPostData = "=" + sData;)?
Many thanks in advance!
The content type x-www-form-urlencoded is a key-value format. With form bodies you are only able to read a single simple type from a request body. As a name is expected, but in this case not allowed, you have to prefix the equal sign to indicate that there is no name with the followed value.
However, you should lean away from accepting simple types within the body of your web-api controller actions.
You are limited to only a single simple type if you attempt to pass data in the body of an HttpPost/HttpPut without directly implementing your own MediaTypeFormatter, which is unlikely to be reliable. Creating a light-weight complex type is generally much more preferable, and will make interoperating with other content-types, like application/json, much easier.

Nancy (C#): How do I get my post data?

I'm using Corona SDK to post data to my C# server:
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["Accept-Language"] = "en-US"
local body = "color=red&size=small"
local params = {}
params.headers = headers
params.body = body
network.request( host .. "/UpdateHand", "POST", nwListener, params )
I receive a message on the server:
Post["/UpdateHand"] = x =>
{
Console.WriteLine("Received ...");
return "Ok";
};
But when I check the data (when I put a breakpoint on it) I don't see where my data is locaded (i.e. the params.body or params.headers). How can I extract this information?
I should POST it correctly according to the documentation on Corona: http://docs.coronalabs.com/daily/api/library/network/request.html
The post data is in
this.Request.Body
If you have suitable type you can deserialize your data to it using model binding:
var x = this.Bind<YourType>();
There is a Nancy extension for this. You will need to include the namespace for it.
using Nancy.Extensions;
var text = Context.Request.Body.AsString();
I like how concise this is, part of Nancy's super-duper easy path.
But a word of caution! This method leaves the stream at the end, so subsequent calls will return empty string. To fix this, always reset the stream immediately afterwards, like so:
Request.Body.Seek(0, SeekOrigin.Begin);
Nancy 2.0 is supposed to correct this so that the stream position is reset by default.
https://github.com/NancyFx/Nancy/pull/2158
This actually works great:
var body = this.Request.Body;
int length = (int) body.Length; // this is a dynamic variable
byte[] data = new byte[length];
body.Read(data, 0, length);
Console.WriteLine(System.Text.Encoding.Default.GetString(data));
For Nancy 2.0.0, Request.Body is a Stream rather than a RequestStream, so doesn't have an AsString method. However, this seems to work:
using (var reqStream = RequestStream.FromStream(Request.Body))
{
var body = reqStream.AsString();
// ... do stuff with body
}
Ideally getting your post data could be accomplished with a simple Bind() call. However, I've seen inconsistent results when using a Bind in a post call such that I've resorted to using the scheme outlined above.
I've seen various discussions about Nancy Bind() working and not working... I've seen both with Post but cannot explain the inconsistency. Where I saw it function properly was where I could guarantee the body of the request was managed as follows:
var data = Encoding.ASCII.GetBytes (postData);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream ()) {
stream.Write (data, 0, data.Length);
}
However, when sending data that should have been similarly handled (though I couldn't confirm) through WSO2 infrastructure (data serialized as a JSON event dictionary sent to a service proxy), Bind failed while the method above succeeded.

Can HttpWebRequest Be Used To Populate a Model?

I am wondering if HttpWebRequest can be used to fill a MVC model? I am trying to build a MVC 4 application where I take data from a college course listing page and massage it in a few different ways in my View. The examples that I have been seeing around have all taken a response stream and returned a string or have not been formatted for MVC (using console.write). Also, as far as I understand, the data as its being returned isn't in a JSON or XML format. Here is my controller so far...
public ActionResult Index()
{
string postData = "semester=20143Fall+2013+++++++++++++++++++++++++++++++&courseid=&subject=IT++INFORMATION+TECHNOLOGY&college=&campus=1%2C2%2C3%2C4%2C5%2C6%2C7%2C9%2CA%2CB%2CC%2CI%2CL%2CM%2CN%2CP%2CQ%2CR%2CS%2CT%2CW%2CU%2CV%2CX%2CZ&courselevel=&coursenum=&startTime=0600&endTime=2359&days=ALL&All=All+Sections";
byte[] dataArray = Encoding.UTF8.GetBytes (postData);
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create("http://www3.mnsu.edu/courses/selectform.asp");
myRequest.Method = "POST";
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.ContentLength = dataArray.Length;
using (WebResponse response = myRequest.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
//insert into a model? Maybe?
}
}
return View();
}
If HttbWebRequest can't be used, is there a way that would work? Or am I completely heading in the wrong direction?
You can using HttpWebRequest and WebResponse to get stream from your college course's web site. Then use HtmlAgilityPack to parse scrap in the stream and insert your desired value into model

How do I get posted data in MVC Action?

I am trying to post some data to a ASP.NET MVC Controller Action. Current I am trying to use WebClient.UploadData() to post several parameters to my action.
The following will hit the action but all the parameters are null. How can get the posted data from the http request?
string postFormat = "hwid={0}&label={1}&interchange={2}localization={3}";
var hwid = interchangeDocument.DocumentKey.Hwid;
var interchange = HttpUtility.UrlEncode(sw.ToString());
var label = ConfigurationManager.AppSettings["PreviewLabel"];
var localization = interchangeDocument.DocumentKey.Localization.ToString();
string postData = string.Format(postFormat, hwid, interchange, label, localization);
using(WebClient client = new WebClient())
{
client.Encoding = Encoding.UTF8;
client.Credentials = CredentialCache.DefaultNetworkCredentials;
byte[] postArray = Encoding.ASCII.GetBytes(postData);
client.Headers.Add("Content-Type", "pplication/x-www-form-urlencoded");
byte[] reponseArray = client.UploadData("http://localhost:6355/SymptomTopics/BuildPreview",postArray);
var result = Encoding.ASCII.GetString(reponseArray);
return result;
}
Here is the Action I am calling
public ActionResult
BuildPreview(string hwid, string
label, string interchange, string
localization) {
... }
When this Action is reached all the parameters are null.
I have tried using the WebClient.UploadValue() and passing the data as a NameValueCollection. This method always returns a status of 500 and because I am making this http request from within the MVC application I cannot find a way to bebug this.
Any help getting this resolved would be super helpful.
-Nick
I corrected the Header to read:
client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
Now UploadData just errors immediately with with server error 500.
Just for laughs have a look in Request.Form and the RouteData in your controller to see if something ended up there.
I was able to get the post xml data from the Request objects InputStream property.
public ActionResult BuildPreview(string hwid, string label, string localization)
{
StreamReader streamReader = new StreamReader(Request.InputStream);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(streamReader.ReadToEnd());
...
}
As a stop-gap measure, you can always change your controller action to accept a FormCollection parameter and then reach in and access the form parameters by name directly.
To get the raw posted bytes from WebClient.UploadData("http://somewhere/BuildPreview", bytes)
public ActionResult BuildPreview()
{
byte[] b;
using (MemoryStream ms = new MemoryStream())
{
Request.InputStream.CopyTo(ms);
b = ms.ToArray();
}
...
}

Using MVC2 ActionResult, how can I redirect (Post) to another site

I have a page that collects data and 'POST's to another site. I could just put he site url in the action of the form tag but I would like to record the information in my database prior to switching sites. In the ActionResult so far I have:
[HttpPost]
public ActionResult MyPage(MyPageModel model)
{
if (ModelState.IsValid)
{
StoreDate(model.fld1, model.fld2)
var encoding = new ASCIIEncoding();
var postData = "";
foreach (String postKey in Request.Form)
{
var postValue = Encode(Request.Form[postKey]);
postData += string.Format("&{0}={1}", postKey, postValue);
}
var data = encoding.GetBytes(postData);
// Prepare web request...
var myRequest = (HttpWebRequest)WebRequest.Create("https://www.site2.com");
myRequest.Method = "POST";
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.ContentLength = data.Length;
// Send the data.
Stream newStream = myRequest.GetRequestStream();
newStream.Write(data, 0, data.Length);
newStream.Flush();
newStream.Close();
Does anyone know how to finish this and use the proper 'return' varient to have this post the data to the other site.
I have edited the snippet based on a response below.
The POST has already happened, so there's not going to be a magic bullet (i.e. a simple ActionResult) that will work for you. Since you're handling the POST response on your server, you'll need to recreate the POST request to the target server yourself. To do that you'll need to leverage an HttpWebRequest vis a vis this answer. After getting the response back from the HttpWebRequest, you'll need to pass that response back, probably via a ContentResult. All in all, it will be non-trivial, but it is possible.
Update:
Based on your snippet, I'd try adding the following:
WebResponse res = myRequest.GetResponse();
StreamReader sr = new StreamReader(res.GetResponseStream());
string returnvalue = sr.ReadToEnd();
return Content(returnValue);
Another option would be to point the form action at the other site and do an ajax post to your server before submitting the form. That would be much easier than playing man-in-the-middle with HttpWebRequest.

Categories

Resources