I'm attempting to build a GET webservice that would from website 1 initiate a GET request...sending that request to website 2 and website two would respond by sending a list of objects. I using Json.net to serialize and deserialize the List of objects.
I've put together a POST webservice with the assistance of this question.. WebService ASP.NET MVC 3 Send and Receive
But I've been unsuccessful so far at adapting that example for my new requirement.
Here is what I have so far from website 1..
public static List<ScientificFocusArea> ScientificFocusAreas()
{
string apiURL = "http://localhost:50328/Api/GetAPI";
//Make the post
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
//var bytes = Encoding.Default.GetBytes(body);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiURL);
Stream stream = null;
try
{
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
request.Timeout = -1;
request.Method = "GET";
}
finally
{
if (stream != null)
{
stream.Flush();
stream.Close();
}
}
List<ScientificFocusArea> listSFA = WebService.GetResponse_ScientificFocusArea(request);
return listSFA;
}
public static List<ScientificFocusArea> GetResponse_ScientificFocusArea(HttpWebRequest request)
{
List<ScientificFocusArea> listSFA = new List<ScientificFocusArea>();
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
{
throw new HttpException((int)response.StatusCode, response.StatusDescription);
}
var end = string.Empty;
using (StreamReader reader = new StreamReader(responseStream))
{
end = reader.ReadToEnd();
reader.Close();
listSFA = JsonConvert.DeserializeObject<List<ScientificFocusArea>>(end);
}
response.Close();
}
}
return listSFA;
}
Then on the website 2...
public class GetAPIController : Controller
{
//
// GET: /Api/GetAPI/
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetScientificFocusAreas()
{
//Get list of SFAs
List<ScientificFocusArea> ListSFA = CreateList.ScientificFocusArea();
string json = JsonConvert.SerializeObject(ListSFA, Formatting.Indented);
//Send the the seralized object.
return Json(json);
}
}
Also, on website 2, I've registered this route for the incoming request...
context.MapRoute(
"GetScientificFocusAreas",
"Api/GetAPI/",
new
{
controller = "GetAPI",
action = "GetScientificFocusAreas",
id = UrlParameter.Optional
}
);
I'm currently getting the error.. he remote server returned an error: (404) Not Found.
Any help would me greatly appreciated.
The problem seems like a routing issue. I would start with the RouteDebugger which can be found here. This tool gives insight into which routes your URL is hitting.
The code I use for a HTTP GET is a bit different that what you have above. It's included below.
public T Get<T>(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
using (Stream responseStream = response.GetResponseStream())
{
if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
{
throw new HttpException((int)response.StatusCode, response.StatusDescription);
}
var end = string.Empty;
using (StreamReader reader = new StreamReader(responseStream))
{
end = reader.ReadToEnd();
reader.Close();
}
responseStream.Close();
response.Close();
JsonSerializer serializer = new JsonSerializer();
serializer.Binder = new DefaultSerializationBinder();
JsonReader jsonReader = new JsonTextReader(new StringReader(end));
T deserialize = serializer.Deserialize<T>(jsonReader);
return deserialize;
}
}
catch (Exception ex)
{
throw new ApiException(string.Format("An error occured while trying to contact the API. URL: {0}", url), ex);
}
}
The other issue I see is in the GetScientificFocusAreas() method. On the second line of the code the objects are converted to JSON. Which is fine, but the last line of code the json is passed into the Json() method. Which converts the string into Json yet again. When using the JSON.Net library use the Content() method in the return instead of Json() and set the content type to application/json
The reasoning for using an external Json converter rather than the internal converter is simply the internal json converter has a few known issues. JSON.Net has been around for years and is solid.
Related
I've got a simple web api which is consumed from a mvc project, I keep on getting the 'Response status code does not indicate success' and was wondering how would I get the response body from the error, I can see the error within a rest viewer but can't navigate through to the error. This is the following code within the MVC app
public ActionResult Index()
{
try
{
var uri = "http://localhost:57089/api/values";
using (var client = new HttpClient())
{
Task<string> response = client.GetStringAsync(uri);
object result = JsonConvert.DeserializeObject(response.Result);
return (ActionResult) result;
}
}
catch (Exception ex)
{
return Content(ex.ToString());
}
return View();
}
Within the API controller I'm sending a bad request, here's the code
public IHttpActionResult Get()
{
return BadRequest("this is a very bad request " + System.DateTime.Now.ToUniversalTime());
}
I've tried to use WebException, HttpRequestException as exceptions to catch the error with no luck.
I can see the response body within the rest viewer
I want to be able to navigate to the Error Message so I can pass that to the client (which later will be changed to a guid).
[EDITED]
I've got a solution without using GetStringAsync, but wanted to use that if possible.
Here's the solution
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(url);
HttpResponseMessage responseMessage = httpClient.GetAsync("").Result;
if (responseMessage.IsSuccessStatusCode) return Content(responseMessage.ToString());
var a = responseMessage.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<HttpError>(a);
object value = "";
return Content(result.TryGetValue("ErrorMessage", out value) ? value.ToString() : responseMessage.ToString());
Is there a better way?
Using WebException you should be able to get to the ResponseStream and the custom error message like this:
catch (WebException e)
{
var message = e.Message;
using (var reader = new StreamReader(e.Response.GetResponseStream()))
{
var content = reader.ReadToEnd();
}
}
Hope that helps.
I am trying to call a Web API method from a handheld device (Compact Framework) with this code:
// "fullFilePath" is a value such as "\Program Files\Bla\abc.xml"
// "uri" is something like "http://localhost:28642/api/ControllerName/PostArgsAndXMLFile?serialNum=8675309&siteNum=42"
SendXMLFile(fullFilePath, uri, 500);
. . .
public static string SendXMLFile(string xmlFilepath, string uri, int timeout)
{
uri = uri.Replace('\\', '/');
if (!uri.StartsWith("/"))
{
uri = "/" + uri;
}
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(xmlFilepath))
{
String line;
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line);
}
byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());
if (timeout < 0)
{
request.ReadWriteTimeout = timeout;
request.Timeout = timeout;
}
request.ContentLength = postBytes.Length;
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded"; // not "text/xml" correct?
try
{
Stream requestStream = request.GetRequestStream();
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();
using (var response = (HttpWebResponse)request.GetResponse())
{
return response.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
request.Abort();
return string.Empty;
}
}
}
Somewhere in SendXMLFile(), it is failing with "NotSupportedException" though... As it's running on a handheld device, I can't put a breakpoint in it and step through it; I could sprinkle a bunch of debug statements throughout (MessageBox.Show()), but I'd rather not do that.
The server code never even reaches the breakpoint I put on the "XDocument doc =" line below:
[Route("api/ControllerName/PostArgsAndXMLFile")]
public void PostArgsAndFile([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
XDocument doc = XDocument.Parse(stringifiedXML);
Is it that the Compact framework can't call a (RESTful) Web API method for some reason? Obviously, the client (handheld/Compact Framework) compiles and runs, it just refuses to actually follow through with the runtime realities of it all.
Does my code require a small alteration for it to fit, or do I need to take a completely different tack?
Web API is not going to be able to handle your body content. You declared it as application/x-form-urlencoded, but it is actually XML formatted and your method signature is expecting it to be a XMLDataContract serialized string.
Instead of using the parameter stringifiedXML, instead, just read the body inside your method..
[Route("api/ControllerName/PostArgsAndXMLFile")]
public async void PostArgsAndFile(string serialNum, string siteNum)
{
XDocument doc = XDocument.Parse(await Request.Content.ReadAsStringAsync());
}
Or event better, use a stream directly.
[Route("api/ControllerName/PostArgsAndXMLFile")]
public async void PostArgsAndFile(string serialNum, string siteNum)
{
XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());
}
This way, you can put the ContentType on the client back to application/xml as it should be.
Using Darrel's code on the server side (I'm using the second one, the Stream), this works on the Client side:
public static string SendXMLFile(string xmlFilepath, string uri, int timeout)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.KeepAlive = false;
request.ProtocolVersion = HttpVersion.Version10;
request.ContentType = "application/xml";
request.Method = "POST";
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(xmlFilepath))
{
String line;
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line);
}
byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());
if (timeout < 0)
{
request.ReadWriteTimeout = timeout;
request.Timeout = timeout;
}
request.ContentLength = postBytes.Length;
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
try
{
Stream requestStream = request.GetRequestStream();
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();
using (var response = (HttpWebResponse)request.GetResponse())
{
return response.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
request.Abort();
return string.Empty;
}
}
}
Which can be called like so:
private void buttonNose_Click(object sender, EventArgs e)
{
String fullFilePath = #"C:\McMurtry\LonesomeDove.XML";
String uri = #"http://localhost:21608/api/inventory/sendxml/ff/gg/42";
SendXMLFile(fullFilePath, uri, 500);
}
In my integration test the object schoolyearCreateRequest sent to /api/schoolyears url contains only null values when passing to the Post([FromBody] SchoolyearCreateRequest request) action parameter.
But when I use fiddler:
http://localhost:6320/api/schoolyears
Content-Type: application/json
Request Body:
{ SchoolyearDto:
{ Id: 10 }
}
Then it works and the SchoolyearDto is not null.
What is the problem in my integration test?
var schoolyearCreateRequest = new SchoolyearCreateRequest
{
SchoolyearDto = new SchoolyearDto(),
SchoolclassCodeDtos = new List<SchoolclassCodeDTO>(),
TimeTablesWeekAddedWeekA = new List<TimeTableDTO>(),
TimeTablesWeekAddedWeekAB = new List<TimeTableDTO>()
};
// Arrange
const string url = "api/schoolyears/";
var request = new HttpRequestMessage(HttpMethod.Post, _server.BaseAddress + url);
request.Content = new ObjectContent<SchoolyearCreateRequest>(schoolyearCreateRequest,new JsonMediaTypeFormatter());
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// Act
var response = _client.PostAsync(_server.BaseAddress + url, request, new JsonMediaTypeFormatter(), new CancellationToken()).Result;
// Assert
Assert.That(response.StatusCode == HttpStatusCode.Created);
UPDATE:
I made it working now in my integration test too:
replace these lines:
request.Content = new ObjectContent<SchoolyearCreateRequest>(schoolyearCreateRequest,new JsonMediaTypeFormatter());
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
with this line:
var postData = new StringContent(JsonConvert.SerializeObject(schoolyearCreateRequest), Encoding.UTF8, "application/json");
Why do I have to serialize the data by myself? And why is nearly nobody doing this approach with web api integration testing? All blogs I read showed the usage of the ObjectContent ??
You can take a look at my answer in the following post:
How do I exercise Formatters in tests using HttpServer?
Also, you can take a look at my blog post which was written long time back, but is still relevant:
http://blogs.msdn.com/b/kiranchalla/archive/2012/05/06/in-memory-client-amp-host-and-integration-testing-of-your-web-api-service.aspx
UPDATE:
Since there seems to be confusion around this, following is a complete example of an in-memory test. Its a bit crude but still should give you an idea.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using WebApplication251.Models;
namespace WebApplication251.Tests.Controllers
{
[TestClass]
public class PeopleControllerTest
{
string baseAddress = "http://dummyhost/";
[TestMethod]
public void PostTest()
{
HttpConfiguration config = new HttpConfiguration();
// use the configuration that the web application has defined
WebApiConfig.Register(config);
HttpServer server = new HttpServer(config);
//create a client with a handler which makes sure to exercise the formatters
HttpClient client = new HttpClient(new InMemoryHttpContentSerializationHandler(server));
Person p = new Person() { Name = "John" };
using (HttpResponseMessage response = client.PostAsJsonAsync<Person>(baseAddress + "api/people", p).Result)
{
Assert.IsNotNull(response.Content);
Assert.IsNotNull(response.Content.Headers.ContentType);
Assert.AreEqual<string>("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());
Person recPerson = response.Content.ReadAsAsync<Person>().Result;
Assert.AreEqual(p.Name, recPerson.Name);
}
}
}
public class InMemoryHttpContentSerializationHandler : DelegatingHandler
{
public InMemoryHttpContentSerializationHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content = await ConvertToStreamContentAsync(request.Content);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Content = await ConvertToStreamContentAsync(response.Content);
return response;
}
private async Task<StreamContent> ConvertToStreamContentAsync(HttpContent originalContent)
{
if (originalContent == null)
{
return null;
}
StreamContent streamContent = originalContent as StreamContent;
if (streamContent != null)
{
return streamContent;
}
MemoryStream ms = new MemoryStream();
await originalContent.CopyToAsync(ms);
// Reset the stream position back to 0 as in the previous CopyToAsync() call,
// a formatter for example, could have made the position to be at the end
ms.Position = 0;
streamContent = new StreamContent(ms);
// copy headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
{
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return streamContent;
}
}
}
i am developing an windows phone 8 app , in my app i am calling services and downloading some data into my app .
i am using httpwebrequest for request, but i am not able to set timeout to my httpwebrequest object.
This is how i have created and used my httpwebrequest :-
public async Task<string> ServiceRequest(string serviceurl, string request, string methodname)
{
string response = "";
try
{
var httpwebrequest = WebRequest.Create(new Uri(serviceurl)) as HttpWebRequest;
httpwebrequest.Method = "POST";
httpwebrequest.Headers["SOAPAction"] = "http://tempuri.org/" + iTestservice + "/" + methodname + "";
httpwebrequest.ContentType = "text/xml";
byte[] data = Encoding.UTF8.GetBytes(request);
using (var requestStream = await Task<Stream>.Factory.FromAsync(httpwebrequest.BeginGetRequestStream, httpwebrequest.EndGetRequestStream, null))
{
await requestStream.WriteAsync(data, 0, data.Length);
}
response = await httpRequest(httpwebrequest);
}
catch (Exception ex)
{
return null;
}
return response;
}
public async Task<string> httpRequest(HttpWebRequest request)
{
string received;
using (var response = (HttpWebResponse)(await Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null)))
{
using (var responseStream = response.GetResponseStream())
{
using (var sr = new StreamReader(responseStream))
{
received = await sr.ReadToEndAsync();
}
}
}
return received;
}
My Doubt is :-
1) How can i set timeout property to Httpwebrequest ??
2)What are the different ways in which i can set the timeout property in my windows phone 8 app ??
Please let me know .
Thanks in Advance.
You can't use HttpWebRequest.Timeout on Windows Phone because it doesn't exist for that platform.
If you're open to using a beta library, you could install HttpClient via NuGet and use its Timeout property.
Otherwise, you're probably best off to use TaskEx.Delay, which is part of Microsoft.Bcl.Async. After installing that library, you would replace this line:
response = await httpRequest(httpwebrequest);
with this:
var httpTask = httpRequest(httpwebrequest);
var completeTask = await TaskEx.WhenAny(httpTask, TaskEx.Delay(5000));
if (completeTask == httpTask)
return await httpTask;
else
return null; // timeout
You can use HttpStatusCode.HttpStatusCode is an enum which can be used to get the type of error in HttpWebRequest.
catch(WebException ex)
{
HttpWebResponse response = (HttpWebResponse)ex.Response;
if(response.StatusCode==HttpStatusCode.GatewayTimeout)
{
}
}
The GatewayTimeout indicates that an intermediate proxy server timed out while waiting for a response from another proxy or the origin server.For more information you can refer to the msdn
site for this.Hope it helps
I have followed the HttpClient samples but couldn't figure it out how to post a method with 2 parameters.
Below is what I tried but it return bad gateway error:
private async void Scenario3Start_Click(object sender, RoutedEventArgs e)
{
if (!TryUpdateBaseAddress())
{
return;
}
Scenario3Reset();
Scenario3OutputText.Text += "In progress";
string resourceAddress = "http://music.api.com/api/search_tracks";
try
{
MultipartFormDataContent form = new MultipartFormDataContent();
// form.Add(new StringContent(Scenario3PostText.Text), "data");
form.Add(new StringContent("Beautiful"), "track");
form.Add(new StringContent("Enimem"), "artist");
HttpResponseMessage response = await httpClient.PostAsync(resourceAddress, form);
}
catch (HttpRequestException hre)
{
Scenario3OutputText.Text = hre.ToString();
}
catch (Exception ex)
{
// For debugging
Scenario3OutputText.Text = ex.ToString();
}
}
I looked all over the internet, but couldn't find any working examples or documents that show how to perform the http post method. Any materials or samples would help me a lot.
Try FormUrlEncodedContent instead of MultipartFormDataContent:
var content = new FormUrlEncodedContent(
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("track", "Beautiful"),
new KeyValuePair<string, string>("artist", "Enimem")
}
);
I prefer to take the following approach where you set the POST data into the request content body. Having to debug it is much easier!
Create your HttpClient object with the URL you're posting to:
string oauthUrl = "https://accounts.google.com/o/oauth2/token";
HttpClient theAuthClient = new HttpClient();
Form your request with the Post method to your url
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, oauthUrl);
Create a content string with your parameters explicitly set in POST data format and set these in the request:
string content = "track=beautiful" +
"&artist=eminem"+
"&rating=explicit";
request.Method = HttpMethod.Post;
request.Content = new StreamContent(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)));
request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
Send the request and get a response:
try
{
HttpResponseMessage response = await theAuthClient.SendAsync(request);
handleResponse(response);
}
catch (HttpRequestException hre)
{
}
Your handler will be called once the request returns and will have response data from your POST. The following example shows a handler that you could put a breakpoint into to see what the response content is, at that point, you could parse it or do whatever you need to do with it.
public async void handleResponse(HttpResponseMessage response)
{
string content = await response.Content.ReadAsStringAsync();
if (content != null)
{
// put your breakpoint here and poke around in the data
}
}