WCF Data Services : how to call POST Operations with big body - c#

How to call WCF Data Services Operations from the WCF Services Client ?
I have this operation on my service :
[WebInvoke(Method="POST")]
public void Add(int ContractId, string Partners)
{
...
}
How do I call this operation from my client ? my client is a C# based application. bearing in mind that the Partners string is a very very long one. it's a concatenation of the partners Ids like : "1,2,3,4, ... 990".
I have tried doing the following :
string requestString = string.Format("Add?contractId={0}&Partners={1}", ContractId, groupIdParam);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl +
requestString);
request.Method = "POST";
var response = request.GetResponse();
but I receive the error : "HTTP 414 : Request Uri too long"

Currently OData doesn't support passing parameters for service operations (that's what the WebInvoke method is) in the body of the POST request. All the parameters are passed in the URL, and thus they have to be pretty small (usually something below 2048 characters, depends on your web server, proxies and so on).

Actually, you aren't performing a POST-request. A POST request provides the information in the request-body, which is why it is used especially for large data-sets. You have to provide both contractId and partners in the request-body. You can use the HttpWebRequest.GetRequestStream() method to get a stream, to which you then write the parameters.
This link http://en.wikipedia.org/wiki/POST_(HTTP) describes the structure used to specify the name-value pairs within the request-body.
So you could write something like this (untested):
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl + "Add");
using (Stream bodyStream = request.GetRequestStream())
using (StreamWriter writer = new StreamWriter(bodyStream))
{
writer.Write("contractId: {0}", contractId);
writer.Write("partners: {0}", String.Join(",", partners);
}
request.GetResponse();
Edit
As Vitek Karas stated this isn't possible. I just looked at it from a client perspective not the service.

Related

Call WebApi from another WebAPI in same application

I have several webAPIs developed in MVC WebAPI such as
public IHttpActionResult SendBroadcast(FinalPayload FinalPayload)
And I want to call another web api from inside this API. All my APIs are in the same application. The signature of the second API is
public IHttpActionResult SendMessage(Notifications)
From the first API i tried
this.SendMessage(Notifications)
and got error message something like reference to static object ....
I wanted to know if this way of calling webAPI is allowed or do I have to invoke a web client or similar such.
Any help in this will be much appreciated.
1st approach
You have to redesign the code in your application i.e. Solution.
Create a class library project. Create an interface for the logic/functions which are common and consumed by different projects.
Implement the interface in the class library project.
In different projects(i.e. WebAPI projects) use the interface to access the common functionality.
2nd Approach
As thought by you, of-course you can create a web client to access the Web API in another project.
Its not a good design and your problem is not ACTUALLY solved (just circumvented).
Poor efficiency, as webclient will use http request to access the code in same solution.
For future maintenance you may end up creating multiple web clients.
No, as usual you'll need to create a new web client in order to consume the API.
What #Marcus H has suggested in the comment is doable too, just wrap the logic inside second API into a separate method (be it instance or static) and invoke it from your first API will do the trick.
Yes you can. You can create a webClient in your controller method method2 that calls method1.
This is a little helper class you could use to help you:
namespace Api.Helpers
{
public static class ApiHelpers
{
private static string HttpGet(string url)
{
WebRequest request = WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
reader.Close();
response.Close();
return responseFromServer;
}
public static string HttpPostJson(string url, string json)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(json);
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
return result;
};
}
}
}
If you want Cookie based authentication, then just use RestSharp instead.
If you use Postman the Chrome extension tool, then if you can access your endpoint, you can automatically generate your C# code.
Simply put the webclient code within the Controller method, and one API call will effectively chain to another.
so in your ApiController you'd write something like:
IHttpActionResult method1() {
var result = ApiHelpers.HttpGet("http://thing.com/test");
return Ok(result);
}

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.

How can I successfully post JSON via C# to a Python Flask route that authenticates via the JSON content?

I've written a rudimentary API in a Flask project to allow POSTing data via JSON strings. The JSON requires two properties: username and apikey, which are validated through the following decorator:
def apikey_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not request.json:
abort(404)
json_data = request.json
if not 'username' in json_data or not 'apikey' in json_data:
abort(404)
user = User.query.filter(User.username == json_data['username']).first()
if not user or user.status != "superuser":
abort(404)
if not user.apikey or user.apikey != json_data['apikey']:
return jsonify({'status': 'error', 'message': 'unrecognized API key'})
return f(*args, **kwargs)
return decorated_function
I've written routes utilizing this decorator and they work beautifully in Python applications: Here's the basic structure of an API route:
#mod.route('/invadd', methods=['GET', 'POST'])
#apikey_required
def invadd():
json = request.json
#lots of application-specific logic that passes unit tests
My Flask unit tests work fine:
good_post_response = self.app.post(
'api/invadd', data=json.dumps(test_json),
content_type='application/json') # assertions which verify the reponse pass
Python applications I've written work fine:
response = urllib2.urlopen(req, json.dumps(post_json)) #req is an api route URL
response_json = json.loads(response.read())
But in my C# app, I get a SocketException: No connection could be made because the target machine actively refused it. when I try to post JSON to these same routes. Here's a relevant code snippet:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create();
request.ContentType = "application/json";
request.Method = "POST";
request.ContentLength = postJSON.Length;
using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) <--- FAILURE POINT
{
sw.Write(postJSON.ToString());
sw.Flush();
sw.Close();
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
string result = sr.ReadToEnd();
AddFeedback(result);
}
I've also tried using the WebClient class:
WebClient wc = new WebClient();
AddFeedback(String.Format("Processing order {0}", order.OrderID));
string postJSON = BuildJSON(order);
string url = String.Format("{0}api/invadd", gslBase); //I verified that the URL is correctly constructed
string response = wc.UploadString(url, "POST", postJSON);
AddFeedback(response);
It's clear to me that the failure happens when the app tries to initiate the connection to the route, as it never gets to the point where it's actually trying to post the JSON data. But I'm not sure how to get around this. Do I need to change my Flask route?
That kind of exception indicates that there was a error at the socket level - that's before you reach JSON, or even HTTP.
Make sure you're connecting to the right machine and the right port.
It's not clear where you're inputting that data on your C# code - you probably should be using WebRequest.Create("yoururl") .
You can also try to connect using a browser, and see if it works.
If all those details are right, you can use wireshark to check what, exactly, is causing the connection to fail

Can I a wrap a GET request inside another GET request?

I may well be approaching this incorrectly, but what I'm trying to do is consume a GET request so I can manipulate the response into a format that I want. I need to be able to launch this from a browser, so I thought about building another RESTful service for this purpose.
i.e. Service ABC returns a string using a GET request. I want to take that string, do some manipulation and return it. I still need to be able to launch from a browser, so what I'm planning is to create a RESTful service XYZ, where the GET request in XYZ launches a call to the GET request of ABC, takes that response, converts it into my collection, and returns that collection. I'll then display in MVC.
Firstly: is this a dumb choice? I don't know a lot about different types of services.
Secondly: I have been able to get the ABC service data using a Console client, but not using a web-based client or service. Is this to be expected?
Code:
string webPath = #"http://ABCService.co.uk/";
string methodCall = #"methodABC/uid";
RestClient restClient = new RestClient(webPath);
RestRequest request = new RestRequest(methodCall, Method.GET);
var restResponse = restClient.Execute(request);
var content = restResponse.Content;
This works fine in the console (and I've actually been able to get the data just using a WebClient too), but neither method works from inside an MVC controller or service. I just get this as an ErrorException in restResponse:
Exception: "{"Unable to connect to the remote server" InnerException
= {"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection
failed because connected host has failed to respond 80.64.52.36:80"}
Service ABC is up and running, can be accessed from a browser and console app. Note that I have no ability to alter any settings within Service ABC.
Many thanks
I resolved this by setting the proxy to null, rather than using a default proxy.
public string ReadWebReport(string path)
{
string str = String.Empty;
HttpWebRequest Request = WebRequest.Create(path) as HttpWebRequest;
Request.Method = "GET"; //Or PUT, DELETE, POST
Request.ContentType = "application/x-www-form-urlencoded";
Request.Proxy = null; //<-- inserted line
using (HttpWebResponse Response = Request.GetResponse() as HttpWebResponse)
{
if (Response.StatusCode != HttpStatusCode.OK)
throw new Exception("The request did not complete successfully and returned status code " + Response.StatusCode);
using (StreamReader Reader = new StreamReader(Response.GetResponseStream()))
{
str = Reader.ReadToEnd();
}
}
return str;
}

Using A Web API for Business Logic?

My web application needs to be able to go and get all my projects from Paymo http://api.paymo.biz/
I am familiar with JSON and XML, but what I'm wondering is, how does one interact with the api (make calls to it).
I would ideally like to make a class in ASP .Net such as PaymoManager(int apikey....)
From there I can wrap the functionality I need. I just need to understand, how do I call functions of the API and how do I get the response. I am not familar with web apis.
Edit: Could you give me an example of this, even with some abstract url. I need this done server side in a CS file.
Basically a simple example that calls someurl.com/somerequest and then how do you receive the JSON or XML... how does this work in terms of a class. I want this in a class.
http://api.paymo.biz/docs/misc.overview.html
To perform an action using the Paymo API, you need to send a request
to the Paymo webservice specifying a method and some arguments, and
will receive a formatted response.
This means that you can use WebClient to download a string from a url:
WebClient client = new WebClient();
string reply = client.DownloadString (address);
Depending on the format you specify, you can parse the reply as XML or JSON.
XDocument xml = XDocument.Parse(reply);
// where ReplyType is a class that defines public
// properties matching the format of the json string
JavaScriptSerializer serializer = new JavaScriptSerializer();
ReplyType abc = serializer.Deserialize<ReplyType>(reply);
If you are using .NET 4.5, you might consider using HttpClient like so:
static async void Main()
{
try
{
// Create a New HttpClient object.
HttpClient client = new HttpClient();
// fill in the details in the following string with your own KEY & TOKEN:
string requestUrl = "https://api.paymo.biz/service/paymo.auth.logout?api_key=API_KEY&format=JSON&auth_token=AUTH_TOKEN"
HttpResponseMessage response = await client.GetAsync(requestUrl );
response.EnsureSuccessStatusCode();
string responseBodyJSON = await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method in following line
// string body = await client.GetStringAsync(uri);
Console.WriteLine(responseBodyJSON );
// Now you can start parsing your JSON....
}
catch(HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ",e.Message);
}
}

Categories

Resources