Is there a way to cancel a specific request? I followed this to cancel request, but what if i have more than one request, how can i be sure that im not canceling the wrong request? Is there a way to cancel a specific request?
Currently it works like this:
async getEmployeeListBySearchword(searchword: string): Promise<IMember[]> {
try {
const response = await this.get(`employees/${decodeURIComponent(searchword)}`);
return JSON.parse(response['response']) as Promise<IMember[]>;
}catch(e){
if(e.responseType != "abort") {
console.log(e);
}
}
}
cancelRequest(){
if(AbortableHttpService.http){
AbortableHttpService.http['pendingRequests'].forEach(request =>{
request.abort();
});
}
}
The http setup code is wrapped into a class named AbortableHttpService, it creates the HttpClient and configures it and hides some of the boilerplate code for it.
I'm not sure if this is documented or not, but in the aurelia-http-client source code, I see that a cancel/abort function is added to the promise you receive when making a request.
So calling either response.cancel() or response.abort() should work.
Related
I am following a sample app from the NDC Oslo which is this app: https://github.com/SteveSandersonMS/presentation-2019-06-NDCOslo/tree/master/demos/MissionControl.
This implements JWT as authentication and authorization. However when I tried to copy the implementation of the code to a Server Side Blazor, I'm getting an error when I try to get the JWT token stored from the local storage described below"
JavaScript interop calls cannot be issued at this time. This is because the component is being
statically rendererd. When prerendering is enabled, JavaScript interop calls can only be performed
during the OnAfterRenderAsync lifecycle method.
Here is my blazor code
protected override async Task OnInitializedAsync()
{
var token = await TokenProvider.GetTokenAsync();
Branches = await Http.GetJsonAsync<List<BranchDto>>(
"vip/api/lookup/getbranches",
new AuthenticationHeaderValue("Bearer", token));
}
The error comes from
public async Task<string> GetTokenAsync()
{
//Code Omitted for brevity
//This line of code is equivalent to the IJSRuntime.Invoke<string>("localstorage.getitem","authToken")
//change to use Blazore.LocalStorage.
var token = await _localStorageService.GetItemAsync<string>("authToken");
return token;
}
I tried perform the code on OnAfterRenderAsync(bool firstRender) and the error is gone but the grid which is binded to the API request has no display. The API request must fill the data source for the grid which must be OnInitializedAsync. Any workaround on this?
Update!
I moved the code OnAfterRenderAsync and added the StateHasChanged Method and I got the desired Behavior.
I forgot that the connection for rendering was a signalR connection.
As per “Detect when a Blazor Server app is prerendering”, you can only safely run interop code in the OnAfterRenderAsync lifecycle method.
However, since this runs after the render cycle, you will need to notify your component to re-render using StateHasChanged() once your asynchronous process completes:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var token = await TokenProvider.GetTokenAsync();
Branches = await Http.GetJsonAsync<List<BranchDto>>(
"vip/api/lookup/getbranches",
new AuthenticationHeaderValue("Bearer", token));
StateHasChanged();
}
}
I have a Sailsjs controller that does some stuff, but currently it is under a policy that requires authentication (i.e. isAuthenticated).
Before having applied that policy, the integration test for my controller worked fine. Now it will not work because it requires the user to login.
For reasons not stated, I cannot make a request to login before each test, so I have to find a way to skip that policy. I would like to stub it so it always lets the route go through. How would you do that in Sailsjs v0.12.11?
You can add an API key in your isAuthenticated method and check for this key. If the key is available, call the next() function.
Code snippet:
module.exports = function (req, res, next) {
if (req.isAuthenticated() || req.headers['api-key'] == 'apiKeyValue') {
return next();
} else {
if (!req.headers['api-key'] && !req.isAuthenticated()) {
return res.redirect('/auth/login');
} else {
res.status(403);
return res.json({
error: 'Invalid Api Key'
});
}
}
So essentially, pass an API key via header and modify the logic in isAuthenticated function. So whenever the API key is available, it will call the next() callback.
I'm using Angular2 with ASP.NET Core MVC and managing manual URL navigation works fine, the server is loading my Home view with Angular2 successfully.
On user authentication, I'm setting up a session variable like this :
HttpHelper.HttpContext.Session.SetString("isLoggedIn", true.ToString() );
What I want is that after the user is in the application, if somewhat he wants to load a specific route by manually navigating to it, I want my service to call my ASP Controller to check if the user already got authenticated so that my guard allows the routing. Instead, the guard is by default set to false and I obviously get redirected to my login page.
Here is my ASP Controller method I want to call to update my IsLoggedIn value in my auth.service :
[HttpGet]
public IActionResult IsConnectedState()
{
if (!String.IsNullOrWhiteSpace(HttpHelper.HttpContext.Session.GetString("isLoggedIn")))
return Ok(true);
else
return Ok(false);
}
so that my AuthenticationGuard can call the AuthenticationService to update the boolean managing the authenticated state :
alert(this.authService.isLoggedIn);
if (!this.authService.isLoggedIn) {
this.authService.setupLoggedInState().subscribe(() => { });
}
with the following code updating the boolean in my auth.service :
setupLoggedInState() {
alert("Setting Up");
// Setting up the Http call
let lControllerAction: string = "/IsConnectedState";
let lControllerFullURL: string = this.controllerURL + lControllerAction;
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
// Call my ASP Controller IsConnectedState() method
return this.http.get(lControllerFullURL, options)
.map((res: any) => {
// Réponse reçue du WebService
let data = res.json();
alert(data);
if (data == true) {
this.isLoggedIn = true;
}
}
).catch(this.handleError);
}
When I do the authentication, and then manually navigate to an URL, my guard tells me that the boolean is indeed set to "false", and I also get the "Setting Up" when my service tries to call http.get.
It seems to bug in the middle of the method, because I never get to the breakpoint I set in my ASP Controller. I get the following error which I don't understand :
"platform-browser.umd.js:937 EXCEPTION: Error: Uncaught (in promise): TypeError: Cannot read property 'toString' of null"
Could it be because I don't call the service at the right moment in my auth.guard ? All my others call such as authentication work with http.post without any issue, so I don't really get where the problem is coming from...
Any help would be greatly appreciated.
There's a known defect in Angular 2 RC5 that causes this error when you do a get and provide the 'Content-Type': 'application/json' header.
For a temporary workaround, add an empty string to the body property on your request options:
let options = new RequestOptions({ headers: headers, body: "" });
I have create an mvc web api 2 webhook for shopify:
public class ShopifyController : ApiController
{
// PUT: api/Afilliate/SaveOrder
[ResponseType(typeof(string))]
public IHttpActionResult WebHook(ShopifyOrder order)
{
// need to return 202 response otherwise webhook is deleted
return Ok(ProcessOrder(order));
}
}
Where ProcessOrder loops through the order and saves the details to our internal database.
However if the process takes too long then the webhook calls the api again as it thinks it has failed. Is there any way to return the ok response first but then do the processing after?
Kind of like when you return a redirect in an mvc controller and have the option of continuing with processing the rest of the action after the redirect.
Please note that I will always need to return the ok response as Shopify in all it's wisdom has decided to delete the webhook if it fails 19 times (and processing too long is counted as a failure)
I have managed to solve my problem by running the processing asynchronously by using Task:
// PUT: api/Afilliate/SaveOrder
public IHttpActionResult WebHook(ShopifyOrder order)
{
// this should process the order asynchronously
var tasks = new[]
{
Task.Run(() => ProcessOrder(order))
};
// without the await here, this should be hit before the order processing is complete
return Ok("ok");
}
There are a few options to accomplish this:
Let a task runner like Hangfire or Quartz run the actual processing, where your web request just kicks off the task.
Use queues, like RabbitMQ, to run the actual process, and the web request just adds a message to the queue... be careful this one is probably the best but can require some significant know-how to setup.
Though maybe not exactly applicable to your specific situation as you are having another process wait for the request to return... but if you did not, you could use Javascript AJAX kick off the process in the background and maybe you can turn retry off on that request... still that keeps the request going in the background so maybe not exactly your cup of tea.
I used Response.CompleteAsync(); like below. I also added a neat middleware and attribute to indicate no post-request processing.
[SkipMiddlewareAfterwards]
[HttpPost]
[Route("/test")]
public async Task Test()
{
/*
let them know you've 202 (Accepted) the request
instead of 200 (Ok), because you don't know that yet.
*/
HttpContext.Response.StatusCode = 202;
await HttpContext.Response.CompleteAsync();
await SomeExpensiveMethod();
//Don't return, because default middleware will kick in. (e.g. error page middleware)
}
public class SkipMiddlewareAfterwards : ActionFilterAttribute
{
//ILB
}
public class SomeMiddleware
{
private readonly RequestDelegate next;
public SomeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
if (context.Features.Get<IEndpointFeature>().Endpoint.Metadata
.Any(m => m is SkipMiddlewareAfterwards)) return;
//post-request actions here
}
}
Task.Run(() => ImportantThing() is not an appropriate solution, as it exposes you to a number of potential problems, some of which have already been explained above. Imo, the most nefarious of these are probably unhandled exceptions on the worker process that can actually straight up kill your worker process with no trace of the error outside of event logs or something at captured at the OS, if that's even available. Not good.
There are many more appropriate ways to handle this scenarion, like a handoff a service bus or implementing a HostedService.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio
The WebAuthenticationBroker doesn't seem to be able to handle navigation to my ms-app://. Just throws this ugly error as you will see below.
Steps
Call AuthenticateAsync(), including callback uri obtained at runtime: WebAuthenticationBroker.GetCurrentApplicationCallbackUri()
Go through authorize process, hit Allow.
Instead of returning, the broker shows the page Can't connect to service. We can't connect to the service you need right now. Unable to do anything, so I hit the Back button visible.
Debugger breaks on catch: "The specified protocol is unknown. (Exception from HRESULT: 0x800C000D)"
The callback for WebAuthenticationBroker.AuthenticateAsync() is received (according to Fiddler4 & the Event Viewer) but it throws the aforementioned exception as if it doesn't know how to interpret the ms-app:// protocol.
All examples imply my code should work but I think there's something less obvious causing an issue.
Code
private static string authorizeString =
"https://api.imgur.com/oauth2/authorize?client_id=---------&response_type=token";
private Uri startUri = new Uri(authorizeString);
public async void RequestToken() {
try {
var war = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.UseTitle
, startUri);
// Imgur knows my redirect URI, so I am not passing it through here
if (war.ResponseStatus == WebAuthenticationStatus.Success) {
var token = war.ResponseData;
}
} catch (Exception e) { throw e; }
}
Event Viewer log excerpts (chronological order)
For information on how I obtained this, read the following MSDN: Web authentication problems (Windows). Unfortunately this is the only search result when querying authhost.exe navigation error.
Information: AuthHost redirected to URL: <ms-app://s-1-15-2-504558873-2277781482-774653033-676865894-877042302-1411577334-1137525427/#access_token=------&expires_in=3600&token_type=bearer&refresh_token=------&account_username=------> from URL: <https://api.imgur.com/oauth2/authorize?client_id=------&response_type=token> with HttpStatusCode: 302.
Error: AuthHost encountered a navigation error at URL: <https://api.imgur.com/oauth2/authorize?client_id=------&response_type=token> with StatusCode: 0x800C000D.
Information: AuthHost encountered Meta Tag: mswebdialog-title with content: <Can't connect to the service>.
Thanks for reading, Stack. Don't fail me now!
Afaik, you need to pass the end URL to AuthenticateAsync even if you assume that the remote service knows it.
The way WebAuthenticationBroker works is like the following: you specify an "endpoint" URL and when it encounters a link that starts with this URL, it will consider the authentication process complete and doesn't even try navigating to this URL anymore.
So if you specify "foo://bar" as callback URI, navigating to "foo://bar" will finish the authentication, as will "foo://barbaz", but not "foo://baz".
Resolved! #ma_il helped me understand how the broker actually evaluates the redirect callback and it led me back to square one where I realized I assumed WebAuthenticationOptions.UseTitle was the proper usage. Not so. Up against Imgur's API using a token, it requires WebAuthenticationOptions.None and it worked immediately.
As an example to future answer-seekers, here's my code.
private const string clientId = "---------";
private static Uri endUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
private static string authorizeString = "https://api.imgur.com/oauth2/authorize?"
+ "client_id="
+ clientId
+ "&response_type=token"
+ "&state=somestateyouwant"
+ "&redirect_uri="
+ endUri;
private Uri startUri = new Uri(authorizeString);
public async void RequestToken() {
try {
WebAuthenticationResult webAuthenticationResult =
await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None
, startUri
, endUri);
if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success) {
string token = webAuthenticationResult.ResponseData;
// now you have the token
}
} catch { throw; }
}