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.
Related
In
ngOninit() of the app.component, we have called an api that basically returns the true/false. Based on that, 'type' a class is injected into the document.body i.e. IsVisible. And in the Sass of app.component it disables the logos and other contents in the web page.
api call
const res= app.services.getIsActiveType().toPromise();
document.body.ClassList.Add(res.type);
in sass
body {
&.Type{
logos {
dispaly: none;
}
}
}
But when deployed to the real server we have an issue with the speed which results in calling and returning the response being delayed by around 6,7 seconds hence the content is visible until the webapi returns a result.
Now, shall we improve the speed of the server or change the logic because the particular role should not see those contents but the slow speed results in showing it for a while.
What should I do? Even if a speed is improved I cannot rely on that.
I searched and solutuon is to use resolvers but I don't know much about that since new to angular.
Approach 1: you can invert the behavior of the CSS class so that it shows instead of hides stuff and keep things hidden by default
Approach 2: you can use APP_INITIALIZER to wait for API's response, the special thing about APP_INITIALIZER is that it will be called even before root component's initialization and angular will wait until it gets resolved, here's the sample code:
const initializer = (apiService: ApiService) => {
return () => {
return apiService.getIsActiveType()
.then(res => res.json())
.then(res => {
document.body.classList.add(res.type);
});
};
}
register this initializer in app module like this:
#NgModule({
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializer,
deps: [ApiService],
multi: true,
},
],
})
export class AppModule { }
if you go with this approach, also optimize API response so that user is not staring at a blank white screen for like 6-7 seconds
Please, try something like:
app.services
.getIsActiveType()
.subscribe( (result:any) => {
document.body.ClassList.Add(result.type);
} );
I would recommend the following:
If content is authorized for a specific user role, that content should not be visible until the role is confirmed.
Utilize Angular's template resources like *ngIf if you want to keep content hidden from the view. Hiding it with CSS is not secure.
I would recommend using observables instead of .toPromise() as this feature will eventually become deprecated in RxJS 7.x (Angular still uses 6.x for now.)
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
I am trying to create a WebHookHandler for Webhooks send from WordPress WooCommerce in ASP.NET C#.
I started with creating a ASP.NET C# Azure API App WebApplication Project and adding the relevant references (Microsoft.AspNet.WebHooks.Common, Microsoft.AspNet.WebHooks.Receivers, Microsoft.AspNet.WebHooks.Receivers.WordPress). Added the WebHookConfig, WordPressWebHookHandler and registered the WebHookConfig in the GlobalAsax.
I then published the application as an Azure App Service.
My WordPressWebHookHandler is still the default of the examples and looks like this:
public class WordPressWebHookHandler : WebHookHandler
{
public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)
{
// make sure we're only processing the intended type of hook
if("WordPress".Equals(receiver, System.StringComparison.CurrentCultureIgnoreCase))
{
// todo: replace this placeholder functionality with your own code
string action = context.Actions.First();
JObject incoming = context.GetDataOrDefault<JObject>();
}
return Task.FromResult(true);
}
}
When testing a User Creation WebHook in WooCommerce I can see the request in the log as below.
But unfortunately it is never received while debugging and I see below error.
I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook. Or possibly it is handled wrong in the routing and ends up in another controller.
Any help is much appreciated.
Your WebHookReceiver is wrong
There is a mismatch of expecting HTML Form Data, when in fact it should be expecting JSON.
WordPressWebHookHandler is still the default
This is what is causing your error. If you look at the WordPressWebHookReceiver, the ReceiveAsync() method implementation, calls out to ReadAsFormDataAsync() method, which is not what you want, as your Content-Type is json. So, you want to be doing ReadAsJsonAsync().
Solution: Don't use the WordPressWebHookReceiver and switch it to another one that will call ReadAsJsonAsync().
Looking at the code
I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook.
You had the right idea, so I dug up some of the code to explain exactly why this was happening.
The code block below is the ReceiveAsync() method that is overridden in the WordPressWebHookReceiver. You can see that it is calling the ReadAsFormDataAsync() which is not what you want...
public override async Task<HttpResponseMessage> ReceiveAsync(
string id, HttpRequestContext context, HttpRequestMessage request)
{
...
if (request.Method == HttpMethod.Post)
{
// here is what you don't want to be called
// you want ReadAsJsonAsync(), In short, USE A DIFFERENT RECEIVER.
NameValueCollection data = await ReadAsFormDataAsync(request);
...
}
else
{
return CreateBadMethodResponse(request);
}
}
A quick search through the repository for classes that call the ReadAsJsonAsync() method, shows that the following recievers implement it:
DynamicsCrmWebHookReceiver
ZendeskWebHookReceiver
AzureAlertWebHookReceiver
KuduWebHookReceiver
MyGetWebHookReceiver
VstsWebHookReceiver
BitbucketWebHookReceiver
CustomWebHookReceiver
DropboxWebHookReceiver
GitHubWebHookReceiver
PaypalWebHookReceiver
StripeWebHookReceiver
PusherWebHookReceiver
I assumed that the CustomWebHookReceiver would fit your requirements, so can grab the NuGet here. Otherwise you can implement your own, or derive it from this class, etc.
Configuring a WebHook Recevier
(Copied from the Microsoft Documentation)
Microsoft.AspNet.WebHooks.Receivers.Custom provides support for
receiving WebHooks generated by ASP.NET WebHooks
Out of the box you can find support for Dropbox, GitHub, MailChimp,
PayPal, Pusher, Salesforce, Slack, Stripe, Trello, and WordPress but
it is possible to support any number of other providers
Initializing a WebHook Receiver
WebHook Receivers are initialized by registering them, typically in
the WebApiConfig static class, for example:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
// Load receivers
config.InitializeReceiveGitHubWebHooks();
}
}
There is a problem with the data format that you send in your request. You must use format of HTML Form as your error message said.
Proper POST data format is described here: How are parameters sent in an HTTP POST request?
Don't forget to set Content-Length header and correct Content-Type if your library doesn't do it. Usually the content type is application/x-www-form-urlencoded.
I would like to make some additions to Svek's answer as I now got my Proof-of-concept completed and understand a bit more about the receivers.
His answer pointed me in the right direction, but needs a little addition.
WordpressWebHookReceiver
Can take in Wordpress Webhooks of type HttpPost. This does not work with Woocommerce as Woocommerce sends Json Webhook messages and will fail the HttpPost validation which is build into the WordpressWebHookReceiver class.
CustomWebHookReceiver
Can take in custom ASP.NET Webhooks. The custom ASP.NET webhooks have a specific partner for validation which includes but is not limited to the 'ms-signature'. Even adding the header will not suffice as the signature is also used in a different way from out of the box Woocommerce to encrypt the message. Basically coming to a point that you can't integrate Woocommerce with the CustomWebHookReceiver without changing the Webhook classes of Woocommerce.
GenericWebHookReceiver
This is the receiver you want, which accepts basically a generic set of Json data and will be able to use the "code" query parameter to verify the secret which you can add in the web.config of your asp.net api application. I used this receiver to finish the Proof-of-concept and got both the signature validation as well as the deciphering of the message working right of the bat.
My basic class which I will start to build into a real solution can be viewed below and changes the JObject into a dynamic object in the methods I call from the class. As you can see I have two methods currently added, one for the customer create and one for the order create to call the respective methods which do an insert into Dynamics 365 (former CRM).
public class GenericJsonWebHookHandler : WebHookHandler
{
public GenericJsonWebHookHandler()
{
this.Receiver = "genericjson";
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
var result = false;
try
{
// Get JSON from WebHook
var data = context.GetDataOrDefault<JObject>();
if(context.Id != "crcu" && context.Id != "cror")
return Task.FromResult(true);
if (context.Id == "crcu")
{
result = WoocommerceCRMIntegrations.Entities.Contact.CreateContactInCRM(data);
}
else if (context.Id == "cror")
{
result = WoocommerceCRMIntegrations.Entities.Order.CreateOrderInCRM(data);
}
}
catch (Exception ex)
{
result = false;
}
return Task.FromResult(result);
}
}
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.
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 ?