5

I have a blazor wasm hosted app where a user can create an account by filling a form. When the account is created, an email is sent to the user with a link s/he must follow to confirm his/her ownership of the email address. the link points to an action method in my server. The same server hosts the blazor wasm app AND the APIs.

The problem is, when the user clicks that link, the request isn't sent to the server. Instead, blazor intercepts the call and tries to route to a page.

How can I make this work ? How can I make a link that point to an action in my server actually make it to the server when clicked ? In other words, how to bypass blazor's routing system, if possible ?

Updates:

  • I'm using Identity as the backing store (This has nothing to do with the problem I have though).
  • I generate the link using the Url property in the base controller Url.Action("actionName", "controllerName", actionParam, Request.Scheme);. The link, for now, looks like https://localhost:5001/api/user/confirmaemail?confirmationtoken=xyz&useremail=abc
  • The Action is a post action.
  • When blazor tries to redirect to the page, it obviously can't because I don't have a page with such a route. The content inside the <NotFound /> component (App.razor file) is therefore displayed.

Note: This is not an error. This is the normal behaviour of balzor wasm. If an app is hosted at www.foo.com, then all the calls to foo.com (www.foo.com/bar for example) won't actually be made to foo.com but will be intercepted by blazor and treated as routes to pages in the app. This is, however, blocking me because my APIs have the same base address as the app (the app is, for now, at localhost:5001/ and the APIs at localhost:5001/api/xyz), hence, blazor is preventing the call to the server when I click the link. The question is how to get arround that ? And if not possible, what other options do I have to implement this flow ?

Abdelhakim
  • 815
  • 1
  • 5
  • 19
  • Do you use identity in your project? How this link request the server(get or post)? And are there any error when redirect to other page? – Karney. Mar 04 '21 at 05:35
  • @Karney. I updated the post to answer your question. – Abdelhakim Mar 04 '21 at 08:03
  • Does this answer your question? [What are the ways for client side blazor to fetch data from server?](https://stackoverflow.com/questions/59224580/what-are-the-ways-for-client-side-blazor-to-fetch-data-from-server) – GSerg Mar 04 '21 at 08:34
  • @GSerg Not really. The question you point to is more about the the difference between blazor wasm (client side) and blazor server side. – Abdelhakim Mar 04 '21 at 08:54
  • It tells you that you have to use HttpClient or the sort to make your POST calls from client-side Blazor. – GSerg Mar 04 '21 at 08:55
  • @GSerg I understand. But i'm not making any call programatically. I'm sending a link to the user's mail that s/he must click on to confirm it's indeed his/her email. Since the link point to an action hosted in THE SAME server as the blazor wasm app, clicking the link won't actually make a call to the action it is pointing to. Instead, blazor is gonna try reroute the user to a page. – Abdelhakim Mar 04 '21 at 09:05
  • https://stackoverflow.com/q/61268117/11683? – GSerg Mar 04 '21 at 09:14
  • @GSerg The question you point to was actually very helpful. Thank you. I posted an answer for my specific problem. – Abdelhakim Mar 04 '21 at 18:56

2 Answers2

3

So I resolved the problem by making the Action method a GET method instead of a POST. It is quite logical when you think about it: when the user clicks the link s/he received by email, a GET will be issued, not a POST.

I Also understood that blazor routes you to a page ONLY when you enter the address in the address bar. If you click a link, the request will be issued. The reason I was thinking blazor was intercepting the request is because clicking the link was redirecting me to a page, but in fact, it was just a fallback mechanism. You can see it in the configure methode:

app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
            endpoints.MapControllers();

            // the link clicked issues a GET. My action is a POST.
            // So no action is found. It falls back to the index.html
            // which is the entery point of my app. Blazor takes over 
            // from here and tries to find a page for the url requested.
            endpoints.MapFallbackToFile("index.html");
        });
Abdelhakim
  • 815
  • 1
  • 5
  • 19
0

I had the same issue. I used a JS Interop to fix it. The code is a shown.

In the /wwwroot/index.html add the below script.

<script>
        function downloadfile(filename, fileContent) {
            var element = document.createElement('a');
            element.setAttribute('href', fileContent);
            element.setAttribute('download', filename);
            document.body.appendChild(element);
            element.click();
            document.body.removeChild(element);
        }
</script>

In the Blazor WASM Razor Component add the below function in @code.

private async void DownloadFile(string fileName)
{
    //Get the file from the Server Side
    //Convert fileName string to String Content
    var response = await httpService.PostAsync(
       url:"/api/download", content:fileNameStringContent);

    if (response.IsSuccessStatusCode)
    {
       var content = await response.Content.ReadAsStringAsync();
       await JSRuntime.InvokeVoidAsync("downloadfile", fileName, content);
    }
}  

In the Server add the below Controller.Action

[HttpPost("download")]
public async Task<string> Download(string fileName)
{
    var fileContent = GetFileAsBytes(fileName);
    
    var base64String = "data:application/pdf;base64," + Convert.ToBase64String(fileContent);
    
    return base64String;
}

private async Task<byte[]> GetFileAsBytes(string fileName)
{
    try
    {
        var folderPath = Path.Combine(_env.ContentRootPath, "folder");\
        if (!Directory.Exists(folderPath))
            return null;

        var filePath = Path.Combine(folderPath, fileName);
        if (!File.Exists(filePath))
            return null;

        var fileBytes = await File.ReadAllBytesAsync(filePath);
        return fileBytes;
    }
    catch
    {
        return null;
    }
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75