I am using Web Api Service to Pass the Data to my Mobile Devices.
There can be 2 scenario for this.
Single Device Requesting Multiple Times
Multiple Device Requesting Multiple Times
In Both the Scenario i am not able to handle multiple Request, I don't know what is the actual problem but it's keep giving me the 403 Response and 500 Response.
I need to handle Multiple request within seconds as I am Dealing with more then 1000 Devices at the same time and side by side i also need to respond them within seconds because we don't want that our devices wait for the Response for more then few Seconds.
Currently I am using Azure platform for the Web Api services and working with MVC 4.0 Using LINQ. If you want my code then i will provide you the Code (I am using repository Pattern in my Project)
Code
Controller :
[HttpPost]
public JObject GetData(dynamic data)
{
JObject p = new JObject();
try
{
MyModelWithObjectInData transactionbatchData = JsonConvert.DeserializeObject<MyModelWithObjectInData>(data.ToString());
return _batchDataService.batchAllDataResponse(transactionbatchData);//Call Repository
}
}
Repository :
public JObject batchAllDataResponse(MyModelWithObjectInData oData)
{
JObject p = new JObject();
var serviceResponseModel = new MyModelWithObjectInData();
using (var transaction = new TransactionScope())
{
//Insert in Tables
}
//Select Inserted Records
var batchData = (from p in datacontext.Table1
where p.PK == oData.ID select p).FirstOrDefault();
if (batchData != null)
{
serviceResponseModel.GetBatchDataModel.Add(new BatchDataModel //List Residing in MyModelWithObjectInData Class File
{
//add values to list
});
}
//Performing 3 Operations to Add Data in Different List (All 3 are Selecting the Values from Different Tables) as i need to Give Response with 3 Different List.
return p = JObject.FromObject(new
{
batchData = serviceResponseModel.GetBatchDataModel,
otherdata1 = serviceResponseModel.otherdata1, //list Residing in my MyModelWithObjectInData Model
otherdata2 = serviceResponseModel.otherdata2 //list Residing in my MyModelWithObjectInData Model
});
I have used below code to track all the request coming through the Service but i am getting error within this.
//here i am getting error
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//var content = request.Content.ReadAsStringAsync().Result;
ServiceTypes myObjs = JsonConvert.DeserializeObject<ServiceTypes>(request.Content.ReadAsStringAsync().Result);
bool result = _serviceLogService.InsertLogs(request, myObjs); //Getting Error while inserting data here on multiple request
return base.SendAsync(request, cancellationToken).ContinueWith((task) =>
{
HttpResponseMessage response = task.Result;
return response;
});
}
Any Help in this would be Much Appreciated.
Providing a code snippet of your data calls from your api service would be most handy. Their are a few ways to handle concurrent queries; if you have not explored it yet the async and await method would be best to leverage in this scenario. But this is from a vague stand point I would need to look at your code to provide you a definitive answer.
Additional information "If you're new to asynchronous programming or do not understand how an async method uses the await keyword to do potentially long-running work without blocking the caller’s thread, you should read the introduction in Asynchronous Programming with Async and Await (C# and Visual Basic)."
Hopefully, this information puts you on the right track.
Best.
I found the Solution using Async Method to Handled Recurring Request of Services
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//Logic to Track all the Request
var response = await base.SendAsync(request, cancellationToken);
return response;
}
My Controller in not able to initiate the context file as request it's already initiated and "concurrent requests" that dead locking my objects because i am getting multiple request from the devices so that i have modified he code to Logs all the Service within SendAsync Method and await helps me to wait until the task completed.
Related
This question already has answers here:
Parsing Json rest api response in C# [duplicate]
(3 answers)
Closed 12 months ago.
tl;dr: I have an API that works in the browser, but I can't figure out how to return a list of items so I can traverse them in a List.
Long version:
I'm still struggling with API's and now I've hit another snag. I have the following code in my API:
[Route("api/[controller]")]
[ApiController]
public class ScheduleController : Controller
{
[HttpGet]
public IEnumerable<Schedule> GetAllSchedules()
{
using (ScheduleDataEntities entities = new ScheduleDataEntities())
{
return entities.Schedule.ToList();
}
}
[HttpGet("{id}")]
public Schedule GetIndividualSchedule(int id)
{
using (ScheduleDataEntities entities = new ScheduleDataEntities())
return entities.Schedule.FirstOrDefault(e => e.ScheduleID == id);
}
}
When I navigate to my localhost (https://localhost:7017/api/schedule) it returns the data just fine:
[{"userID":121,"weekDay":"Monday","startTime":"07:00:00","endTime":"07:15:00","createdDate":"2022-01-18T22:11:34.8966667","modifiedDate":"2022-01-18T22:11:34.8966667","active":false,"scheduleID":14414,"dayOfWeek":1,"tsType":"Normal"},
{"userID":94,"weekDay":"Wednesday","startTime":"08:45:00","endTime":"09:00:00","createdDate":"2021-11-03T13:50:50.26","modifiedDate":"2021-11-03T13:50:50.26","active":false,"scheduleID":13160,"dayOfWeek":3,"tsType":"Normal"}
...]
So far, so good. Now, I want to return the list to the client calling the API. I've made a simple test client that should get the list when I press a button:
client.Dispose();
client = new HttpClient();
client.BaseAddress = new Uri("https://localhost:7017/api/schedule/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
List<Schedule> list = GetScheduleList("https://localhost:7017/api/schedule");
//Do something with the list...
However, I can't figure out how this method should work:
static List<Schedule> GetScheduleList(string path)
{
List<Schedule> schedules = null;
HttpResponseMessage response = client.GetAsync(path);
return schedules;
}
Every tutorial I can find online, says to use GetAsync. However, that only works with the await keyword. Adding the await keyword means I have to add the async keyword to my method, which means I must return a Task<List> or a task-like type, which can't be converted to a List. If I return it as a Task, how can I then break it back down into my list of schedules?
I really feel like I've tried anything I've come across, so any help you can give is much appreciated.
try this
static async Task<List<Schedule>> GetScheduleList(string path)
{
var response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var stringData = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<Schedule>>(stringData);
}
return null;
and fix the method call
List<Schedule> list = await GetScheduleList("https://localhost:7017/api/schedule");
or you can try, if you don't use async
List<Schedule> list = GetScheduleList("https://localhost:7017/api/schedule").Result;
did you try to start the web app in debug mode and find the return of the API ?
You can even use console.log to give more details;
Otherwise, I was there one day. it's CORS problem. you should add the header : Access-Control-Allow-Origin should have wildcard "*" as a value in order to be able to call https APIs on http servers.
I have an Angular application running on top of the .NET MVC application (.NET Framework 4.8) which sends the http requests to the API (also .NET Framework 4.8) and from there, the requests are sent to other APIs. I want to be able to pass the cancellation token to all those APIs, when the request is cancelled in the browser.
In Angular I'm using switchMap operator but I also tried simple unsubscribe and in both cases it works the same way - cancellation token is correct in the MVC app, but it doesn't seem to be passed to the APIs. Here is my code (simplified):
Angular
private request$ = new Subject();
private response$ = this.request$.pipe(switchMap(() => this.apiService.getData()));
// in other part of the code, I'm subscribing response$ and triggering the next request
// when needed (sometimes previous request is not completed yet).
// This successfully cancels request in browser.
this.request$.next()
MVC
[HttpGet]
public async Task<SomeClass> Get(string url, CancellationToken ct)
{
using (HttpClient client = new HttpClient())
{
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new System.Uri(url)
};
// this part works fine i.e. IsCancellationRequested is set
// to true when request is cancelled
if(!cancellationToken.IsCancellationRequested) {
var response = await httpClient.SendAsync(proxyRequest, HttpCompletionOption.ResponseContentRead, ct);
// ...
// more code here
// ...
}
}
}
API
[HttpGet]
public async Task<IHttpActionResult> GetData()
{
CancellationToken cancellationToken = Request.GetOwinContext().Request.CallCancelled;
if(!cancellationToken.IsCancellationRequested) {
// get data, make request to another API, process and return data
}
}
I have also tried following in the API
[HttpGet]
public async Task<IHttpActionResult> GetData(CancellationToken cancellationToken)
{
if(!cancellationToken.IsCancellationRequested) {
// get data, make request to another API, process and return data
}
}
But the cancellationToken.IsCancellationRequested is always false in APIs (unless the 45s timeout happens). Any ideas how to resolve this?
I'm running through a list of both secure and unsecured domains (both http:// and https://) in an array and wish to return their status codes. How is this possible in ASP.Net Core 1.0?
so far, I have
foreach(var item in _context.URLs.ToList())
{
// Do something here with item.Domain to check for status
// For example, if item.Domain was https://example.com..
}
I tried it with regular ASP.Net syntax with this approach:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(item.Domain);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
The problem is, GetResponse doesn't work in ASP.Net Core
Can anyone help me out with an effecient solution so that the variable returned would be the status?
ex: 200, 500, 404..
EDIT - Here is my full controller AND SOLUTION:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using MyApp.Models;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
namespace MyApp.Controllers.Api
{
public class URLsController : Controller
{
private MyAppDBContext _context;
public class newLink
{
public string Domain { get; set; }
public HttpStatusCode Status { get; set; }
}
public async Task<HttpStatusCode> GetStatusCodes(string url)
{
var client = new HttpClient();
var response = await client.GetAsync(url);
return response.StatusCode;
}
public URLsController(MyAppDBContext context)
{
_context = context;
}
[HttpPost("api/URLs")]
public async Task<IActionResult> Post(string url)
{
if (url != "")
{
// I pass a URL in through this API and add it to _context.URLs..
// I execute a status code check on the URLs after this if statement
}
List<newLink> list = new List<newLink> ();
foreach (var item in _context.URLs.ToList())
{
newLink t = new newLink();
t.Domain = item.Domain;
t.Status = await GetStatusCodes(item.Domain);
list.Add(t);
}
return Ok(list);
}
}
}
This returns an array back in this format:
[{"Domain":"https://example1.com/","Status":200},
{"Domain":"https://example2.com/","Status":200},
{"Domain":"https://example3.com/","Status":200}]
You could use HttpClient as it is easier to work with (you don't need to catch WebExceptions for the non success status codes like you would have to do with the plain HttpWebRequest and then extract the HTTP status code from the exception).
You could write a helper method that given a list of urls will return a list of corresponding status codes. This will make your code a little bit more decoupled. Do not violate the single responsibility principle. A method should not do more than 1 specific thing (in your example you were mixing some DB calls and HTTP calls into a single method which is bad practice).
public async Task<IList<HttpStatusCode>> GetStatusCodes(IList<string> urls)
{
var client = new HttpClient();
var result = new List<HttpStatusCode>();
foreach (var url in urls)
{
var response = await client.GetAsync(url);
result.Add(response.StatusCode);
}
return result;
}
Remark 1: If the url that you are trying to call is not DNS resolvable or your calling application doesn't have network access to the specified address on the target port you will not get any status code for obvious reasons. You will get a nice exception. Think about handling this case. It's up to you to decide what you want to return in the resulting collection in this case.
Remark 2: Making a GET request just for determining the HTTP status code might be a waste as you are throwing away the response body that you have already transported over the wire. If the remote resource responds to HEAD requests that might be more efficient way to determine if the server is alive. But consider this with caution as it will depend on the specifics of the web endpoint that you are calling.
Remark 3: You have undoubtedly noticed that this method is async. Well, if you intend to develop under .NET Core you'd better get accustomed to it. You could of course violate the built-in asynchronous patterns that the framework provides you by blocking the calling thread:
var urls = _context.URLs.ToList();
IList<HttpStatusCode> statusCodes = GetStatusCodes(urls).GetAwaiter().GetResult();
But this is an extremely bad practice. A more idiomatic way of working is to make all your methods asynchronous through the entire chain until you reach the main calling method which is usually provided by the framework itself and which can be asynchronous as well. For example if you are calling this inside a Web API action you could simply make it async:
[HttpGet]
[Route("api/foos")]
public async Task<IActionResult> Get()
{
var urls = _context.URLs.ToList();
IList<HttpStatusCode> statusCodes = await GetStatusCodes(urls);
return this.Ok(statusCodes);
}
I am using MVC 5 on .Net and I have a user flow that looks like this:
User enters address into an form.
Form gets posted to controller using AJAX.
Controller records address into the database.
Controller makes a WebClient request to Bing Maps to geocode the address into latitude and longitude.
Controller records latitude and longitude to the database.
Controller returns an AJAX result that is rendered client-side into the updated view with the address and the latitude/longitude.
I know that the call to Bing Maps should happen in an async context so that my site's speed is uncoupled from that of Bing Maps.
Instead I think my flow should work like this:
User enters address into an form.
Form gets posted to controller using AJAX.
Controller records address into the database.
Controller launches an async task to do the geocoding and update the database
Controller returns an AJAX result to the client that shows the updated address and tells it to poll client-side for the completion of the geocode result.
I am stuck on step #4. Here is what I have:
public ActionResult GetLocation(int id)
{
Listing li = db.Listings.Find(id);
Task.Run(() => {
// update geocode if necessary
if (li.BizAddress.GeoStatus != BusinessAddress.GeocodeStatus.UpToDate &&
DateTime.Now - li.BizAddress.LastGeoAttempt > TimeSpan.FromHours(1))
{
Geocoder geo = new Geocoder();
GeocodeResult gr = geo.Geocode(li.BizAddress).Result;
if (gr.BadResult != true)
{
li.BizAddress.Latitude = gr.Location.Latitude;
li.BizAddress.Longitude = gr.Location.Longitude;
li.BizAddress.GeoStatus = BusinessAddress.GeocodeStatus.UpToDate;
}
else
{
// failed
li.BizAddress.Latitude = 0;
li.BizAddress.Longitude = 0;
li.BizAddress.GeoStatus = BusinessAddress.GeocodeStatus.BadResult;
}
li.BizAddress.LastGeoAttempt = DateTime.Now;
db.SaveChanges();
}
});
return PartialView("~/Views/Listing/ListingPartials/_Location.cshtml", li);
}
However I get an exception that the db context has been disposed of when I get to db.SaveChanges().
I want my Task to run async inside of a closure so that db is still a valid undisposed variable.
Is this possible? I'm new to async programming and I don't know all of the idioms yet.
Additional info: I am rendering my panel in my View like this:
#{Html.Action("GetLocation", new { id = Model.ID });}
I want to keep this behavior since loading it with AJAX may hurt SEO.
Firing off a Task in this way is not good practice. Instead, mark your ActionResult signature as async and then await the results that you need from inside the method body. This way you won't be tying up server resources waiting for the response from Bing. That also frees you up from having to worry about instructing the client side to poll for a future result as I would consider this an anti-pattern in async programming. It's much easier to just deal with it all in one call and send the result when it's available. Since you're using EF6 you can also leverage async methods to improve server performance.
public async Task<ActionResult> GetLocation(int id)
{
var listing = await db.Listings.FindAsync(id);
...
var gr = await geo.Geocode(li.BizAddress);
At the very least, if you're sure you want to go down this path its important to clean up your Task code. It's very bad practice to use .Result with Tasks inside of a web server as it locks up server threads. Change the definition of your Task so that it is async:
Task.Run(async () => {
// update geocode if necessary
if (li.BizAddress.GeoStatus != BusinessAddress.GeocodeStatus.UpToDate &&
DateTime.Now - li.BizAddress.LastGeoAttempt > TimeSpan.FromHours(1))
{
Geocoder geo = new Geocoder();
GeocodeResult gr = await geo.Geocode(li.BizAddress);
I need to integrate a third party's Web API methods into a WCF service.
The WCF Service will be called by another external party, and they expect the call and return to be synchronous.
I am able to get results from the API with the usual RestClient.ExecuteAsync.
I put together the following for synchronous calls:
public static List<Books> GetSyncBooks(int companyId)
{
var response = GetSynchronousBooks(companyId);
var content = response.Result.Content;
List<Books> result = new List<Books>();
return Helpers.JSONSerialiser.Deserialize<BookList>(content);
}
async private static Task<IRestResponse> GetSynchronousBooks(int companyId)
{
var request = BuildGETRequest("Book", companyId);
var response = await RestSharpHelper.ExecuteSynchronousRequest(request);
return response;
}
public static Task<IRestResponse> ExecuteSynchronousRequest(RestRequest request)
{
var client = new RestClient(BaseUrl);
client.AddHandler("application/json", new RestSharpJsonDotNetDeserializers());
var tcs = new TaskCompletionSource<IRestResponse>(TaskCreationOptions.AttachedToParent);
client.ExecuteAsync(request, (restResponse, asyncHandle) =>
{
if (restResponse.ResponseStatus == ResponseStatus.Error)
tcs.SetException(restResponse.ErrorException);
else
tcs.SetResult(restResponse);
});
return tcs.Task;
// BREAKPOINT here shows TASK value as
// Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
}
The problem, however, is that I never get a result using this. The response content is always null. What am I doing wrong?
EDIT: Thanks Stephen. I have seen your name on some of the questions here on this subject: your score seems to indicate you know your way around this. I have indeed implemented the wcf service calls as you indicated based on your answer at another question. Can I ask you a related follow-up question? How scalable are async WCF service calls like this example? Would it "just work" for multiple simultaneous calls in the range of 10 to 100 per second, ignoring the processing overhead downstream?
I assume that your WCF service is hosted in ASP.NET.
Your problem is here: response.Result. I explain this deadlock situation on my blog. In summary, await will capture the current "context" (in this case, an ASP.NET request context) and will use that to resume the async method. However, the ASP.NET request context only allows one thread at a time, so if the request thread is blocked (calling response.Result), then the async method can never continue and you get a deadlock.
The solution is to correct this misunderstanding:
The WCF Service will be called by another external party, and they expect the call and return to be synchronous.
Since you're dealing with a client/server scenario, you don't have to make it synchronous. The asynchrony of the client is completely independent from the asynchrony of the server.
So, just implement your WCF service asynchronously:
public static Task<List<Books>> GetBooksAsync(int companyId)
{
var response = await GetBooksAsync(companyId);
var content = response.Content;
List<Books> result = new List<Books>();
return Helpers.JSONSerialiser.Deserialize<BookList>(content);
}
The client can still call it synchronously if they wish to.