HttpClient .Result hangs when [WebMethod] attribute used - c#

First i think i need to declare im a C# novice. I'm a JS and CSS dev with a fair bit of C so this is relatively new territory.
So what i have is an ASP.NET page where i'm making an AJAX request to the code behind, this is then making a request to a WebAPI service to download a zip file to the code-behind, the client does not need to receive the zip.
When i stick the code below into the Page_Load it all works fine and i get a byte array of the zip file. However when used in the method with [webmethod] attribute it hits the webAPI service but hangs. When the service returns nothing happens.
It locks up on line var res = client.GetAsync("/someURl").Result.
I have control over the WebAPI but as its returning fine and everything works fine when the attribute is not used i don't believe the issue is on that end. However i can post related code from there too if needed.
So i have two questions, firstly what on earth could be going on cause this behavior?
Second i've got a good handle on garbage collection is JS when working with closures etc but not here. Ive heard conflicting advice that i should and shouldn't use the using keyword on the HttpClient object. I'm not using a single client object throughout but creating a new one every time the ajax method is hit. So having using is right here isn't it?
EDIT:
The delegate handler is adding some headers to the request to deal with authentication that's all.
[WebMethod]
public static bool SyncApplicant(int id)
{
var serviceOne = DIFactory.Resolve<IServiceOne>();
var settings= serviceOne .GetCompanySettings();
try
{
var delegatingHandler = new WebApiDelegatingHandler((Guid)settings.AppId, settings.ApiKey);
using (var client = HttpClientFactory.Create(delegatingHandler))
{
client.BaseAddress = new Uri(settings.ApiUrl);
using (var res = client.GetAsync("/someURl").Result)
{
var d = res.Content.ReadAsByteArrayAsync().Result;
}
}
}
catch (Exception ex)
{
var x = ex;
return false;
}
return true;
}
Thanks for any advice.

Most probably, that deadlock is being cause by the ASP.NET synchronisation context (see details here].
You can try using ConfigureAwait(false); on the async call to avoid the async continuation trying to acquire the synchronisation context.

I don't know how you would use AJAX to let a user download a file in a browser. You can use a standard HTML link to a new .aspx page and put your Web API-calling code in Page_Load of the new page you're linking to.

Related

Angular 8 - Why client makes multiple API calls to back-end

I have a classic client application with API calls to the server to make operations on DB. But for some reason, every client method in service makes two calls to my controller when I need just once.
What's the reason, why and how I could fix it? Moreover, the second call comes also if the back-end didn't return anything yet but still performing operation.
That's some code example:
Method calls the service:
export class TestingComponent implements OnInit {
results: any;
constructor(
private testingservice: TestingService) { }
ngOnInit() {
let test = true;
this.startingMethod(test);
}
startingMethod(test) {
this.testingservice.getData(test).subscribe( data => {
this.results = data;
})};
}
Service method:
export class TestingService{
constructor(private configService: ConfigService, private http: HttpClient) { }
getData(test: boolean): Observable<any> {
let urlc = this.configService.getCurrentEndpoints();
let params = new HttpParams();
params = params.set('test', test);
return this.http.get<any>(`${urlc.baseUrl}${urlc.getdata}`, {'params': params });
}
}
I hope was clear enough, but if I don't freely ask me more details. Thanks a lot!
Seems like it's a bug from the browsers, they sends the second request to get the favicon of the page, and since they don't have it, it just brings anything.
This is a link for the Chrome bug.
https://bugs.chromium.org/p/chromium/issues/detail?id=39402
Firefox, and most other browsers, also send out a request for a favicon when they first connect, but cache the result i.e. if there isn't a favicon returned first time, they don't keep trying - which is why you're only seeing a single request from Firefox. It seems Chrome is unfortunately a little too persistent with its favicon requestiness.
There may be two reasons for this case:
As you mentioned that there are two calls for BE, maybe one of them is preflight request.
You can read about this here: https://livebook.manning.com/book/cors-in-action/chapter-4/
The second reason may be multiple subscriptions:
You can change the calling of service call like:
startingMethod(test) {
this.testingservice.getData(test).toPromise().then( data => {
this.results = data;
})}
or you can use a subscription object like:
subscription = new Subscription();
startingMethod(test) {
this.subscription.add(
this.testingservice.getData(test).subscribe( data => {
this.results = data;
}));
}
ngOnDestroy(){
this.subscription.unsubscribe();
}
Moreover, the second call comes also if the back-end didn't return anything yet but still performing the operation.
I suspect that you are refering to the OPTIONS request. That is a CORS preflight request that is generated by the browser itself which is totally normal.
OPTIONS requests are what we call pre-flight requests in Cross-origin resource sharing (CORS).
They are necessary when you're making requests across different origins in specific situations.

How to configure web api to respond multiple client at the same time?

I'm setting up a new server for web api, but when I try to make a post request from two individual client, server only responds to the first one. From second one, I always get 500 Internal Server Error.
I tried to make all methods in the server as async but same error has occured.
I call the web service as below:
using(var client = new HttpClient())
{
client.BaseAddress = new Uri("http://serverdomain/ApiName/")
var response = client.PostAsync("controller/method",
keyvaluepairContent);
result = response.Result.Content.ReadAsAsync<List<string>>().Result;
}
And the relevant service code is below:
[Route("controller/method")]
[AcceptVerbs("POST")]
public List<string> Foo([FromBody] someparams)
{
//some logic
}
I wrote the config file as :
config.Routes.MapHttpRoute(
name : "DefaultApi",
routeTemplate : "{controller}/{action}/{id}",
defaults : new { id = RouteParameter.Optinal }
);
For one client at the same time, the server is working very well. I get what I need. However, when two clients make requests even for different methods, the delayed one always gets 500 Internal Error. Debugger says that this code below cannot parse the result, that is beacuse the response is not a string list but the error above.
result = response.Result.Content.ReadAsAsync<List<string>>().Result;
I think my code is fine, but I need to configure my web api. I did searched about it but no result.
As mentioned in a comment first, you are using HttpClient wrong please look at this.
As for your WebAPI to be able to respond to multiple calls asynchronously you need to wrap your code in an appropriate method signature like so:
[Route("controller/method")]
[AcceptVerbs("POST")]
public async Task<IHttpActionResult> Foo([FromBody] someparams)
{
//some logic
}
Some valuable info from another stackoverflow answer here
And another simpler answer here
Like you mentioned in your question you tried making all the methods async but the issue is that you need to use Task

How to get data from another page

The functionality exists in pageB.aspx and the parameters are passed from pageA. I'm failing to get the result of pageB. What should be done here.
pageA.aspx.cs:
string URI = Server.MapPath("~/pageB.aspx");
NameValueCollection UrlParams = new NameValueCollection();
UrlParams.Add("section", "10");
UrlParams.Add("position", "20");
using (WebClient client = new WebClient())
{
byte[] responsebytes = client.UploadValues(URI, "POST", UrlParams);
string responsebody = Encoding.UTF8.GetString(responsebytes);
}
when the compiler reads byte[], there is a dialog which asks if changes should be made to pageB. On clicking No, nothing happens. The string 'responsebody' is empty.
pageB.aspx.cs::
protected void Page_Load(object sender, EventArgs e)
{
if (int.TryParse(Request.QueryString["section"], out section)
&& int.TryParse(Request.QueryString["position"], out position))
{
ProcessData();
}
}
private string ProcessData()
{
string HTMLContent = string.Empty;
try
{
//some code to render the string.
return HTMLContent;
}
catch (Exception ex)
{
throw ex;
}
}
I would like to get 'HTMLContent' from pageB
Use HttpContext.Current.Request["Key"] instead of Request.QueryString["Key"].
if (int.TryParse(HttpContext.Current.Request["section"], out section)
&& int.TryParse(HttpContext.Current.Request["position"], out position))
{
ProcessData();
}
Having pageA 'load' PageB via a web request is probably not the best idea in the world.
My reasoning for this is that I have seen it done on various projects and you can run into all sorts of problems with the routing of the request and permissions.
Essentially the PageA->PageB request will be treated like a new user loading PageB, cookies, session variables etc will be lost unless you explicitly populate them. firewall rules, routing, load balancing etc will act in odd ways.
Any of these problems could cause your unexpected behavior of PageA
Instead : Move the functionality out of pageB into a service object and call it directly from where needed.
However : Say for some reason you are required to do it via a web request. Perhaps its not always on the same server or something.
It looks from your sample code like you are treating PageB as a webservice. You pass in the parameters and use the whole response. So you could if required expose the underling code, lets call it ServiceB as a WebServiceB and then reference it from PageA via the web request.
This would be 'allowed' as the encapsulation of the data as a WebSerivce make it obvious that PageB is not a page of itself and should not rely on variables such as session, cookies etc which are not explicitly passed in.
Further more the calling code, 'knows' that the webservice could be on any machine in the world and thus won't expect it to know about things unless they are explicitly passed.
If you do change PageB to a service and you still need a stand alone PageB which returns HTML. Simply make a new PageB which calls ServiceB behind the scenes. This allows you to separate your 'view' logic from the data/logic provided by the service
Try using Session if you want to pass any type of data between different pages.
EDIT :
Since Session wont be working in your case, you could also try using WebCache instance to store information and access it across other pages.
This link might help here : MSDN : WebCache
Hope this helps.

DotNetOpenAuth - OpenAuth2 in C# - ASP.NET MVC

I'm rewriting some old application, written in ASP.NET MVC. It used authentication by LDAP, but now it is necessary to rewrite it to OAuth2. I've decided to use DotNetOpenAuth library as it looked like best choice, but I'm stuck on auth response.
Currently, I have one controller class called AccountController, containing some methods, but most important are RedirectToIS and PostAuth (which is an redirect uri):
public ActionResult RedirectToIS()
{
DotNetOpenAuth.OAuth2.AuthorizationServerDescription asd = new DotNetOpenAuth.OAuth2.AuthorizationServerDescription();
String site = ConfigurationManager.AppSettings["oauth:site"];
asd.AuthorizationEndpoint = new Uri(site + "/oauth/authorize");
asd.TokenEndpoint = new Uri(site + "/oaut/token");
asd.ProtocolVersion = DotNetOpenAuth.OAuth2.ProtocolVersion.V20;
this.oaclient = new DotNetOpenAuth.OAuth2.WebServerClient(asd, ConfigurationManager.AppSettings["oauth:appid"], ConfigurationManager.AppSettings["oauth:secret"]);
this.oaclient.RequestUserAuthorization(null, new Uri("http://localhost/Account/PostAuth"));
return View();
}
The PostAuth method is what I am trying to make to work correctly. It is page, where the OAuth2 server redirect user after successful authorization with code and state GET parameters. Because I'm trying to utilize library and not (re)write it, I've stuck here - I don't know what to do now.
I tried, as I've seen in one example, use DotNetOpenAuth.OAuth2.IAuthorizationState st = this.oaclient.ProcessUserAuthorization(); in PostAuth, but it don't work. In example author had original WebServerClient instance, but I can't achieve it with Session nor using AccountControler data item.
So, finally, my question: How to transfer object oaclient from RedirectToIS method to PostAuth method (some kind of session?) or how to start using OAuth?
PS: I'm not new to C#, but I've never used ASP.NET.

In .NET MVC 4 (Web API), how do I intercept a request for a controller and change it's Content-Type?

Thanks for looking!
Background
I am writing an API layer for a company that will be used by disparate apps and external consumers.
On the consumption end, most consumers will call a service via ajax using a jQuery $.post(); however, Internet Explorer makes our lives more challenging (of course!). For IE, I must use the XDomainRequest object because IE will not run a jQuery $.post() and because if I use IE's XMLHttpRequest(), I get a security message which is unacceptable (again--of course!):
Otherwise, XMLHttpRequest() works fine.
I am using C#, .NET MVC 4 (WebApi)
Problem
The problem is that XDomainRequest does not allow you to set the Content-Type header and always defaults to text-plain which MVC 4 WebApi controllers will not accept (and yet again--of course!!).
Question
How can I intercept requests for my controllers, detect the presence of text-plain content types and change them to text-json content-type on the fly?
Thanks in advance!
Well after two days and pouring over documentation and reading in this thread I've been able to make this work. So please forgive me if my description of the solution is poor; this is the first time I answer one of these types of threads. Since it took me so long to find the problem I figured it is worth saving some soul from falling into this same problem.
The source for my help came from the above link by byterot.
First thing I did was to create a DelegatingHandler. So in my helper folder or where every you want to create a class and call it what you want.
Here is mine:
public class ContentTypeHandler : DelegatingHandler
{
/** Check that this is an IE browser. */
if ((request.Headers.UserAgent.ToString().IndexOf("MSIE", System.StringComparison.Ordinal) > -1))
{
MediaTypeHeaderValue contentTypeValue;
if (MediaTypeHeaderValue.TryParse("application/json", out contentTypeValue))
{
request.Content.Headers.ContentType = contentTypeValue;
request.Content.Headers.ContentType.CharSet = "utf-8";
}
}
/** Return request to flow. */
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
// work on the response
var response = task.Result;
return response;
});
}
Last think that you have to do is call the Handler in your Global.asax.cs file inside your Application_Start():
GlobalConfiguration.Configuration.MessageHandlers.Add(new ContentTypeHandler());
That's all I did and it worked. So good luck I hope this helps someone.
There is no problem modifying request in HTTP stack. It can be done by writing and registering your custom DelegatingHandler before it gets to the controller. Delegating handler can take care of this early-on in the game, so your request will arrive to the controller in the form you want it to. It could be route-specific handler as well.
http://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler.aspx
Did you try $.ajax instead of $.post ?

Categories

Resources