In the context of .NET...
For a web application I'm developing, I have the following architecture (from topmost to bottommost, loosely speaking):
Web API 2.0 controllers in the Web layer
Service layer (more or less one-to-one with controllers; e.g. ProjectsController calling into a ProjectsService)
Repository layer, which incorporates the Model layer and references to the .NET RavenDB Client
Ninject for DI
All calls by the controllers into the service layer return something: Create returns the object POST-ed; Update returns the object PUT-ed; Delete returns a copy of the object DELETE-ed, etc.)
All calls from the client (javascript in the browser) into the Web API are AJAX
Focusing now on one controller in particular and two services, I have the following: A ProjectsController ("PC") calling into the ProjectsService ("PS"), which has a SmartyStreetsGeocodingService ("SSGS") injected into it and that the ProjectsService calls. I wrote SSGS as a synchronous service initially just for testing purposes. But now the time has come to convert the sync call out to the Smarty Streets RESTful API to an async call. I'm having a timing issue right now that is related to synchronicity, and the GeocodingResult it processes too soon.
My question really revolves around architecture. Also, I would like to stay within the confines of just converting from sync to async (I'm aware of other approaches, such as .NET Service Bus, SignalR, etc., all of which I'll look at later).
What exactly should be async?
Consider the following code from the SSGS:
using (var response = request.GetResponse() as HttpWebResponse)
{
if (response == null) return geocodingResult;
var jsonSerializer = new DataContractJsonSerializer(typeof(CandidateAddress[]));
var stream = response.GetResponseStream();
if (stream == null) return geocodingResult;
var objResponse = jsonSerializer.ReadObject(stream);
jsonResponse = objResponse as CandidateAddress[];
}
if (jsonResponse == null || jsonResponse.Length == 0) //<-- Executes too soon
{
geocodingResult.IsVerified = false;
return geocodingResult;
}
geocodingResult.IsVerified = true;
The fragment above is what I wish to convert to async as the portion of code immediately following the using statement executes too soon. But that leads to questions:
When I do convert to async, what should be asynchronous exactly?
Just the method in which the call to SSGS is located?
Should the PS itself make an async call into the SSGS?
Does the PC need to be async as well?
Should the call from the PC into the PS be async?
Thank you.
The most important thing that should be asynchronous in your case is the HTTP call to the remote service. From there on, everything that is calling this service should of course be asynchronous.
At the end of the day you should have an asynchronous controller action in your outermost layer (the Web API):
public async Task<HttpResponseMessage> Get()
{
var request = WebRequest.Create("http://google.com");
using (var response = await request.GetResponseAsync())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
string responseContent = reader.ReadToEnd();
return Request.CreateResponse(HttpStatusCode.OK, responseContent);
}
}
So as you can see from this controller action I have used an asynchronous Httprequest. So in order to achieve that in your current design you need to make everything asynchronous and expose async methods in your service layer as well so that you can async/await on the result that's gonna get you the JSON from the service layer.
In my example I have simply returned a string from the remote HTTP call but you could easily adapt your service layer to return complex objects as well:
public async Task<MyModel> GetMyModelfromRemoteServiceCall();
{
var request = WebRequest.Create("http://google.com");
using (var response = await request.GetResponseAsync())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
string responseContent = reader.ReadToEnd();
return JsonConvert.DeserializeObject<MyModel>(responseContent);
}
}
You could then trivially easy await on this service layer method from your async Web API controller action.
Related
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);
}
I have tried to create a simple console application.
We have a call system from 8x8 that provide a web streaming API but their documentation is very limited and nothing in C#.
The api service streams call statuses in near real time and I would like to get that 'stream' and be able to read and process it in realtime if possible. The response or Content Type is 'text/html'. But the actual body of the response can be declared as json - sample below:
{"Interaction":{"attachedData":{"attachedDatum":[{"attachedDataKey":"#pri","attachedDataValue":100},{"attachedDataKey":"callingName","attachedDataValue":999999999999},{"attachedDataKey":"cha","attachedDataValue":99999999999},{"attachedDataKey":"cnt","attachedDataValue":0},{"attachedDataKey":"con","attachedDataValue":0},{"attachedDataKey":"med","attachedDataValue":"T"},{"attachedDataKey":"pho","attachedDataValue":9999999999},{"attachedDataKey":"phoneNum","attachedDataValue":9999999999},{"attachedDataKey":"tok","attachedDataValue":999999999}]},"event":"InteractionCreated","inboundChannelid":9999999999,"interactionEventTS":9999999,"interactionGUID":"int-15b875d0da2-DJOJkDhDsrh3AIaFP8VkICv9t-phone-01-testist","resourceType":0}}
I have seen several posts concerning httpClient and the GetAsync methods but none of these appear to work as they appear to be for calls when a response is made, not something that constantly has a response.
Using fiddler for the call it does not appear to close so the stream is constantly running, so fiddler does not display any data until a separate user or instance connects.
When I use a browser the content is 'streamed' to the page and updates automatically and shows all the content (as above).
The api contains authentication so when another client connects and retrieves data the connected client closes and finally I am able to see the data that was gathering.
This is the code so and does return the big stream when another client connects but ideally I want a real time response and appears to just get stuck in the GETASYNC method:
var response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = await responseContent.ReadAsStringAsync();
Console.WriteLine(responseString);
}
Hopefully that's enough information for one of you clever people to help me in my predicament.
I was also having an issue consuming their streaming API and the examples I found that worked with the Twitter and CouchBase streaming API's did not work with 8x8. Both Twitter and CouchBase send line terminators in their pushes so the solution relied on ReadLine to pull in the feed. Since 8x8 does not send terminators you'll need to use ReadBlock or better ReadBlockAsync.
The following code shows how to connect using credentials and consume their feed:
private static async Task StreamAsync(string url, string username, string password)
{
var handler = new HttpClientHandler()
{
Credentials = new NetworkCredential {UserName = username, Password = password},
PreAuthenticate = true
};
// Client can also be singleton
using (var client = new HttpClient(handler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Connection.Add("keep-alive");
using (var response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead))
{
using (var body = await response.Content.ReadAsStreamAsync())
{
using (var reader = new StreamReader(body))
{
while (!reader.EndOfStream)
{
var buffer = new char[1024];
await reader.ReadBlockAsync(buffer, 0, buffer.Length);
Console.WriteLine(new string(buffer));
}
}
}
}
}
}
I started down the path of using HttpClient as I thought the service I was accessing is a REST service. Turns out it's a JSON service running on port 80 but is a socket application.
The HttpClient opens the remote port but when it sends the JSON request it never gets a response. I was having the hardest time getting fiddler to get a response back as well. But I was able to get wget and curl to send/receiving a response. That's when I talked to the original developer and he mentioned that it wasn't a true "REST" service, but just a socket application that sends/receives JSON.
Is there something I can do to tweak HttpClient to access a socket application or am I going to have to take a step back and use WebSockets?
This is the test code that sends/receives the JSON packet.
private async Task ProcessZone(string szIPAddress)
{
string responseData = string.Empty;
Uri baseAddress = new Uri(#"http://" + szIPAddress + "/player");
try
{
using (var httpClient = new HttpClient { BaseAddress = baseAddress })
{
var _req = new SendRequest();
_req.id = "rec-100";
_req.url = "/stable/av/";
_req.method = "browse";
var _json = JsonConvert.SerializeObject(_req);
using (var content = new StringContent(_json,Encoding.UTF8, "application/json"))
{
using (var response = await httpClient.PostAsync(baseAddress, content))
{
responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
var _Response = JsonConvert.DeserializeObject<Response>(responseData);
var item = new ZoneInfo();
item.szIPAddress = szIPAddress;
item.szZoneName = _Response.result.item.title;
lstZones.Add(item);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private async Task ProcessZones()
{
foreach (var item in ZoneSearch)
{
await ProcessZone(item.IPAddress);
}
}
The connection hangs on this line:
using (var response = await httpClient.PostAsync(baseAddress, content))
I should also mention that the code above does work fine on a true rest service...
That's when I talked to the original developer and he mentioned that it wasn't a true "REST" service, but just a socket application that sends/receives JSON.
Knowing the protocol is the first step towards making a working client.
Is there something I can do to tweak HttpClient to access a socket application or am I going to have to take a step back and use WebSockets?
Neither, unfortunately. HttpClient - as the name implies - only works with HTTP services. Since the server is not an HTTP server, it won't work with HttpClient. WebSockets have a rather confusing name, since they are not raw sockets but instead use the WebSocket protocol, which require an HTTP handshake to set up. Since the server is not an HTTP/WebSocket server, it won't work with WebSockets.
Your only choices are to either pressure the developer to write a real REST service (which makes your job orders of magnitude easier), or use raw sockets (e.g., Socket). Correctly using raw sockets is extremely difficult, so I recommend you pressure the developer to write a REST service like the entire rest of the world does today.
I'am trying to pass values from a controller to another controller in another domain. I'am adding data to a NameValueCollection and pass it to another controller [httppost] method and receiving data there mapped to a Model same as i passed from.
Currently i'am running it locally by opening two instance of VS simultaneously. When the both VS is opened the values are passed correctly and the information is written to db correctly and i receive a response like "{byte[0]}". Now when i try stopping the destination controller Project and try to submit data then it wont work but still i get the same response as "{byte[0]}". Can somebody please help me how to return the response command in this scenario. Is there a way a understand the UploadValues are completed or not completed.
.........
.........
NameValueCollection resumeDetails = new NameValueCollection();
resumeDetails.Add("FirstName", "KRIZTE");
byte[] res = this.Post(ConfigurationManager.AppSettings["RedirectionUrl"].ToString(), resumeDetails);
return View("Index");
}
public byte[] Post(string uri, NameValueCollection resumeDetails)
{
byte[] response = null;
WebClient client = new WebClient();
response = client.UploadValues(uri, resumeDetails);
return response;
}
You should not use the WebClient because of problems like this.
Microsoft implemented HttpClient class as a newer API and it has these benefits:
HttpClient is the newer of the APIs and it has the benefits of
has a good async programming model
1- being worked on by Henrik F Nielson who is basically one of the inventors of HTTP, and he designed the API so it is easy for you to follow the HTTP standard, e.g. generating standards-compliant headers
2- is in the .Net framework 4.5, so it has some guaranteed level of support for the forseeable future
3- also has the xcopyable/portable-framework version of the library if you want to use it on other platforms - .Net 4.0, Windows Phone etc.
so I'm gonna show you an example of using HttpClient:
var uri = "http://google.com";
var client = new HttpClient();
try
{
var values = new List<KeyValuePair<string, string>>();
// add values to data for post
values.Add(new KeyValuePair<string, string>("FirstName", "KRITZTE"));
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
// Post data
var result = await client.PostAsync(uri, content);
// Access content as stream which you can read into some string
Console.WriteLine(result.Content);
// Access the result status code
Console.WriteLine(result.StatusCode);
}
catch(AggregateException ex)
{
// get all possible exceptions which are thrown
foreach (var item in ex.Flatten().InnerExceptions)
{
Console.WriteLine(item.Message);
}
}
We are using an HttpClient to post json to a restful web service. In one instance, we are running into something that has us baffled. Using tools like postman, fiddler etc, we can post to an endpoint and see that it is working. When we do the same with HttpClient.PostAsJsonAsync, we can verify in the software we are posting to that it received the data just fine. However, our PostAsJsonAsync will always eventually time out rather than give us a response.
We have worked with the team that created the service we are consuming, plus our additional testing on our side, and we have not yet been able to truly time out that service.
Every time we do a post with HttpClient, we then can verify that the target software we post to does indeed get the data. Any time we do a post to that target software from any other tool, we always very quickly see a response with status code of 200. Something about HttpClient is failing to accept the response from this particular service. Does anyone have an idea what we can look at from here?
Here's the code (though it is so cookie cutter that I hardly feel it is needed)
public string PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = client.PostAsJsonAsync(useUrl, o).Result;
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return response.Content.ReadAsStringAsync().Result;
}
return "";
}
}
This:
var response = client.PostAsJsonAsync(useUrl, o).Result;
Is causing you code to deadlock. This is often the case when blocking on async API's, and that's why you're experiencing the "I don't see any response coming back" effect.
How is this causing a deadlock? The fact that you are executing this request in an environment that contains a synchronization context, perhaps one which belongs to the UI. It's executing the async request, and when the response arrives, it continues via an IO completion thread which attempts to post the continuation onto that same UI context, which is currently blocked by your .Result call.
If you want to make an HTTP request synchronously, use WebClient instead. If you want to take advantage of the async API properly, then await instead of block with .Result.
I had the same issue and this SO answer fixed it for me.
In a nutshell, you have to use the ConfigureAwait(false) extension to avoid the deadlock:
var response = await client.PostAsJsonAsync(useUrl, o).ConfigureAwait(false);
Is there a reason why you're not following the async await pattern? You're calling an async method, but not awaiting it. You didn't say if the code calling your REST service was a Windows Forms or ASP.NET application, but that .Result is probably causing you issues.
Can you restructure your method like this:
public async Task<string> PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
This is a slight modification to #Justin Helgerson's solution. There are 2 blocking .Result calls in your method; once you go async you should fix them both.
public async Task<string> PostDataAsync(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
Note I've also renamed the method to PostDataAsync in accordance with the TAP pattern.
System.Net.ServicePointManager.Expect100Continue = false;
That one line of code in our case fixed the problem. A developer from a different team offered that suggestion, and it works. I have yet to google it and read up on it enough to offer an explanation of what that is addressing.