Error 503. The service is unavailable (blazor server side)
See original GitHub issueDescribe the bug
Using HttpClient multiple times in a row causes responses to start lagging and eventually return HTTP Error 503. The service is unavailable despite the api service reporting no issues at all and keeps responding to other calls. Asp.net core version: 3.0.100-preview4-011223
To Reproduce
Please note that the code is a bit messy due to troubleshooting. Here’s the code: The service that is injected into the .razor page
public async Task<PagedResult<ProductViewModels.Details>> GetProductsAsync(int? pageIndex, int itemsPerPage)
{
var url = $"{_baseUrl}/products/list/{pageIndex},{itemsPerPage}";
try
{
var response = await _client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var jsonString = await response.Content.ReadAsStringAsync();
var jObj = (JObject) JsonConvert.DeserializeObject(jsonString);
var paged =
JsonConvert.DeserializeObject<PagedResult<ProductViewModels.Details>>(
jObj["payLoad"].ToString());
return paged;
}
else
{
var msg =
$"Unsuccessful request to {url} {response.StatusCode} {await response.Content.ReadAsStringAsync()}";
_logger.LogError(
msg);
throw new Exception(msg);
}
}
catch (Exception e)
{
_logger.LogError($"Exception when trying to get product records from {url}", e);
throw;
}
}`
The .razor page:
@page "/products"
@using Kiss.Application.Features.Products.ViewModels
@using Microsoft.AspNetCore.Components
<DataTable Items="@_products" TItem="ProductViewModels.Details">
<HeaderTemplate>
<th>Name</th>
<th>Description</th>
</HeaderTemplate>
<RowTemplate Context="Item">
<td>@Item.Name</td>
<td>@Item.Description</td>
</RowTemplate>
<FooterTemplate Context="Item">
<td colspan="2">@_itemsTotal products found.</td>
</FooterTemplate>
</DataTable>
<Paginator ref="_paginator" LastPageIndex="@_lastPageIndex" />
@functions {
private int _itemsPerPage = 5;
private int _lastPageIndex = 0;
private IEnumerable<ProductViewModels.Details> _products = new List<ProductViewModels.Details>();
private int _itemsTotal = 0;
private Paginator _paginator = new Paginator();
[Inject] IProductService ProductService { get; set; }
protected override void OnAfterRender()
{
_paginator.PageChanged += PageChangedEvent;
}
public async void PageChangedEvent(int pageIndex)
{
await UpdateProducts(pageIndex, _itemsPerPage);
}
protected override async Task OnInitAsync()
{
await UpdateProducts(0, _itemsPerPage);
}
private async Task UpdateProducts(int pageIndex, int itemsPerPage)
{
if (ProductService != null)
{
var result = await ProductService.GetProductsAsync(pageIndex, itemsPerPage);
if (result != null)
{
_products = result.Items;
_itemsTotal = result.ItemsTotal;
_itemsPerPage = result.ItemsPerPage;
_lastPageIndex = result.TotalPages - 1;
StateHasChanged();
}
}
}
}
The paginator that triggers the api calls
@using Microsoft.AspNetCore.Components
<div class="dataTables_paginate paging_simple_numbers" id="DataTables_Table_0_paginate">
<ul class="pagination">
<li class="paginate_button page-item previous @Disabled(0)" id="DataTables_Table_0_previous">
<a aria-controls="DataTables_Table_0" data-dt-idx="0" tabindex="0" class="page-link" onclick="@FirstPage">First</a>
</li>
<li class="paginate_button page-item previous @Disabled(0)" id="DataTables_Table_0_previous">
<a aria-controls="DataTables_Table_0" data-dt-idx="1" tabindex="0" class="page-link" onclick="@PreviousPage">Previous</a>
</li>
<li class="paginate_button page-item next @Disabled(LastPageIndex)" id="DataTables_Table_0_next">
<a aria-controls="DataTables_Table_0" data-dt-idx="2" tabindex="0" class="page-link" onclick="@NextPage">Next</a>
</li>
<li class="paginate_button page-item next @Disabled(LastPageIndex)" id="DataTables_Table_0_next">
<a aria-controls="DataTables_Table_0" data-dt-idx="3" tabindex="0" class="page-link" onclick="@LastPage">Last</a>
</li>
</ul>
</div>
@functions
{
private int _currentPageIndex = 0;
[Parameter]
private int LastPageIndex { get; set; }
[Parameter]
public Action<int> PageChanged { get; set; }
void ChangeCurrentPageIndex(int pageIndex)
{
if (pageIndex != _currentPageIndex)
{
_currentPageIndex = pageIndex;
PageChanged?.Invoke(_currentPageIndex);
}
}
string Disabled(int pageIndex)
{
return _currentPageIndex == pageIndex ? "disabled" : "";
}
void FirstPage()
{
ChangeCurrentPageIndex(0);
}
void NextPage()
{
if (_currentPageIndex < LastPageIndex)
{
ChangeCurrentPageIndex(_currentPageIndex + 1);
}
}
void PreviousPage()
{
if (_currentPageIndex > 0)
{
ChangeCurrentPageIndex(_currentPageIndex - 1);
}
}
void LastPage()
{
ChangeCurrentPageIndex(LastPageIndex);
}
}
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
services.AddScoped<HttpClient>(s =>
{
// Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
var uriHelper = s.GetRequiredService<IUriHelper>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.GetBaseUri())
};
});
services.AddScoped<IProductService, ProductService>();
services.AddRazorPages();
services.AddServerSideBlazor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
loggerFactory.AddSerilog();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
Symptom
After the page loads the table of records loads correctly. When clicking on Next and Previous repeatedly to go back and forth (not particularly fast), it takes about 3 iterations for the response to start lagging and eventually stop with an error. I hard coded the api endpoint and tried it in isolation and it’s responding very quickly without any issues (hardcoded or not). I’ve searched a lot to see if my setup is incorrect but can’t find anything that would suggest so at this point. One minor thing to note is that the event handler in the products.razor page is async void, which I thought may be a reason.
Exceptions Thrown
[2019-05-06T20:23:43.734Z] Error: System.Exception: Unsuccessful request to http://localhost/kiss.api/products/list/0,5 ServiceUnavailable <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Service Unavailable</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Service Unavailable</h2>
<hr><p>HTTP Error 503. The service is unavailable.</p>
</BODY></HTML>
at Kiss.Web.Services.ProductService.GetProductsAsync(Nullable`1 pageIndex, Int32 itemsPerPage) in C:\dev\TVProjects\Kiss\Kiss.Web\Services\ProductService.cs:line 54
at Kiss.Web.Pages.Products.UpdateProducts(Int32 pageIndex, Int32 itemsPerPage) in C:\dev\TVProjects\Kiss\Kiss.Web\Pages\Products.razor:line 50
at Kiss.Web.Pages.Products.PageChangedEvent(Int32 pageIndex) in C:\dev\TVProjects\Kiss\Kiss.Web\Pages\Products.razor:line 38
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteSynchronously(TaskCompletionSource`1 completion, SendOrPostCallback d, Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.ExecuteBackground(WorkItem item)
and
blazor.server.js:1 Uncaught (in promise) Error: Cannot send data if the connection is not in the 'Connected' State.
at e.send (blazor.server.js:1)
at e.sendMessage (blazor.server.js:1)
at e.send (blazor.server.js:1)
at Object.beginInvokeDotNetFromJS (blazor.server.js:8)
at c (blazor.server.js:8)
at Object.s [as invokeMethodAsync] (blazor.server.js:8)
at blazor.server.js:8
at e.onEvent (blazor.server.js:8)
at e.onGlobalEvent (blazor.server.js:8)
Expected behavior
Since the api service is working fine, the expectation is that I should be able to use the pagination to go back and forth without a degradation/lagging and eventually error that requires a page reload.
Screenshots

Additional context
This is the first time I post on GitHub, hope I did it right and hope for a solution. Apologies if I made a mistake here.
Thanks
Issue Analytics
- State:
- Created 4 years ago
- Comments:24

Top Related StackOverflow Question
Ok, problem resolved. My bad. The issue was with OnAfterRender. This is of course triggered multiple times. The solution to this problem is as follows:
Pass in the event handler as a param to the child:
Remove the OnAfterRender override method.
`` Invoke the passed in event handler of the parent in the child:
Thanks! This can be closed.
Misuse by registering parent component event handler to child component in parent OnAfterRender instead of passing in the parent component event handler as a param to the child component.