404 Deploying asp.net core hosted blazor webassembly to Netlify - asp.net-core

I am attempting to deploy an asp.net core hosted blazor webassembly app to Netlify. I have published the release version of the Server project to a directory on my desktop, and uploaded it to github. I set Netlify's publish directory to wwwroot and the site does render just fine. However, if I attempt a call to an api controller, it returns a 404. Specifically, here is my code:
//Register.razor in the Client project
if (Model.Password.Length >= 6 && Model.Password == Model.ConfirmPassword)
{
await HttpClient.PostAsJsonAsync<RegisterModel>("api/Register/Post", Model);
NavigationManager.NavigateTo("/");
}
//In my controller
[Route("api/Register")]
public class RegisterController : Controller
{
private UserContext UserContext { get; set; }
private IHasher Hasher = new Pbkdf2Hasher();
public RegisterController (UserContext userContext)
{
UserContext = userContext;
}
[RequireHttps]
[HttpPost]
[Route("Post")]
public async Task Post([FromBody]RegisterModel model)
{
var user = new UserModel
{
Email = model.Email,
Password = Hasher.Hash(model.Password)
};
await UserContext.AddAsync(user);
await UserContext.SaveChangesAsync();
}
}
The url request I send is: https://(NetlifyDefaultDomain)/api/Register/Post. However, I get a 404 response. On localhost it works just fine. I'm imagining that there's a setting that I have to change somewhere in order for the request URL to work. I've tried looking but have been unable to find guidance. What do I need to change? Thanks
Edit
Here is the Program.cs file of my Client project
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddTransient(sp => new HttpClient {
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider,
ApiAuthenticationStateProvider>();
builder.Services.AddScoped<IAuthService, AuthService>();
await builder.Build().RunAsync();
}
}
Target framework is netstandard2.1, and it's webassembly 3.2.0.

Netlify is a static file host. They do not run any server-side applications like .NET core on their servers.
So you can host your Blazor client-side application on Netlify but if you want server side code to run you must host it somewhere else.
If you are looking for free hosting for your API there are some cloud providers that have a free tier. Azure has free App Service with some limits, Google cloud has a free micro VPS that can host a small app, heroku also has a free offering.
A cheap VPS from Digital Ocean, Vultr or Linode is another alternative.

Related

Add Blazor Client to ASP .Net Core Web API (.NET 6)

I was wondering if there was a specific way to add a Blazor front end to an ASP .NET Core project. I know when creating an Blazor WebAssembly App there is the option to select ASP.Net Core Hosted and it would create a template, but I would like to add the client to the host manually.
Specifically, I've been looking at this ASP.Net to ASP.Net Core tutorial here and would like to add a Blazor front-end to the ASP.NET Core Web API project that is created.
Sorry in advance if the question isn't clear, this is my first time doing this sort of project.
If you want to add the API to the host manually you can create an ASP.NET Core Web API project. Create your post/get/ect controllers in the Web API, and in the Blazor Project inject an HttpClient with a BaseUri that points at your Web API controllers.
This is how I set mine up too. I keep all my components and services separate in class library and razor library projects. Let me know if you need further assistance.
Update
Seems like their might be some confusion on the project layout. For this example I named my Blazor Server App BlazorServerFrontEnd. I named my ASP.NET Core Web API BlazorServerBackEnd. I created mine on .NET Core 6.0. It doesn't use a Startup.cs like 3.1. Instead everything is done in the Program.cs file.
BlazorServerBackEnd (Web Api) will not have services.AddServerSideBlazor() like our BlazorServerFrontEnd (Blazor Server App). If you're seeing this in both projects you probably selected the wrong type. When you run BlazorServerBackEnd (Web Api) you'll see it has one controller built in (Controllers/WeatherForecastController.cs).
BlazorServerFrontEnd (Blazor Server App) will have services.AddServerSideBlazor(). You'll see in Data folder there is also a WeatherForecatService.cs. When you look at the service it doesn't connect to any Api. The service just grabs the data from a hardcoded readonly string[].
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
This is what we need to change. Instead of the data being fetched from within BlazorServerFrontEnd (Blazor Server App) we want to connect to our BlazorServerBackEnd (Web Api). Open up Program.cs in the BlazorServerFrontEnd (Blazor Server App) and change the injected WeatherForecastService from a Singleton to an HttpClient.
//builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddHttpClient<WeatherForecastService>(client =>
{
// Path pointing to BlazorServerBackEnd (Web Api) baseUri
client.BaseAddress = new Uri("https://localhost:7246/");
});
Open up WeatherForecastService.cs in the BlazorServerFrontEnd (Blazor Server App) and add a constructor to inject our HttpClient that we just setup. We'll use the HttpClient to connect to our BlazorServerBackEnd (Web Api) using _httpClient.GetAsync("WeatherForecast"). Now it looks like this;
public class WeatherForecastService
{
private readonly HttpClient _httpClient;
public WeatherForecastService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
var response = await _httpClient.GetAsync("WeatherForecast");
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
WeatherForecast[] forecasts = JsonConvert.DeserializeObject<WeatherForecast[]>(json);
return forecasts;
}
return null;
}
}
Please keep in mind this is a very brief example to get you on your feet. Ideally you'll want to have an interface for your services, and you'll want to inject the interface into your pages instead of the class. So in the real world it would look more like this;
builder.Services.AddHttpClient<IWeatherForecastService, WeatherForecastService>(client =>
{
// Path pointing to BlazorServerBackEnd (Web Api) baseUri
client.BaseAddress = new Uri("https://localhost:7246/");
});
And you would be injecting your interface into the page like so;
#inject IWeatherForecastService ForecastService
I also recommend separating components, services, controllers, models ect. into seperate razor/class libraries. Keep everything very loosely coupled. This will make it easy for you to port your application over to something like a MAUI or Windows Forms App BlazorWebView.
P.S. Swagger comes built into the Web Api project. It is very useful when developing and debugging. I would recommend familiarizing yourself with it if your new.

Dotnet site behind kestrel stops working (Requests return 503 from Apache server)

I run .NET core 3.1 app behind in Linux machine with Kestrel behind reverse proxy (Apache).
I constantly push some data to certain endpoint and then relay that data through signalR hub to users:
[HttpPut("{tagName}/live")]
[Authorize(Roles = "Datasource")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[TypeFilter(typeof(TagControllerFilterAttribute))]
public async Task<IActionResult> PutLiveTag(Tag tag, string tagName)
{
bool status = await tagRepository.UpdateLiveData(tagName, tag);
if (status)
{
await tagHubContext.Clients.Group(tagName).SendAsync(tagName, tag);
return Ok();
}
return BadRequest();
}
Users joins and leaves that signalr hub by simply removing them from group:
public class TagDataHub : Hub
{
[HubMethodName("subscribe")]
public async Task JoinTagGroup(string tagName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, tagName);
}
[HubMethodName("unsubscribe")]
public async Task LeaveTagGroup(string tagName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, tagName);
}
}
My app runs in deployment for few hours and then when I try use that app's API endpoints or anything related to that app I get 503 response from originated from Apache. I really don't know why Kestrel stops working...
When I run service dotnet-site status I get that app is still running.
I checked processes in server, I see that dotnet uses lots of resources, but apart from code provided above there is nothing else running in that app:
Please send help I really don't know how to debug/figure out this on linux server, I would value any suggestion.
I also looked this up and did exactly as this person, but it didn't help me: apache mpm worker

How can I host ASP.NET API and Blazor Web Assembly like an JavaScript-SPA?

Context:
We want to create a Single Page Application that runs with Blazor WebAssembly on the client-side. On the server-side, the solution has an ASP.NET MVC which includes some ApiController classes for our REST APIs.
We want to use ASP.NET API on the server-side instead of Blazor Server because we want to provide a REST interface with ApiController classes for unknown consumers.
Here is my client-side (Blazor WebAssembly) and server-side (ASP.NET API) project in a single solution:
A first try to request the API via BlazorĀ“s HttpClient-class in our FetchData-component:
#inject HttpClient Http
...
#code {
private TodoItem[] TodoItems;
protected override async Task OnInitializedAsync()
{
TodoItems = await Http.GetJsonAsync<TodoItem[]>("api/ToDo");
}
}
On server-side the API-Controller looks like:
namespace ToDoListAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Produces("application/json")]
public class ToDoController : ControllerBase
{
[HttpGet]
public string IGetAll()
{
var lResult = new List<ToDoList.TodoItem>();
// create dummies
for (var i = 0; i < 10; i++)
{
lResult.Add(new ToDoList.TodoItem() { Title = $"Title {i}", IsDone = false });
}
return JsonSerializer.Serialize(lResult);
}
}
}
Problem: In my Blazor WebAssembly Project the request to the API fails. The Blazor WebAssembly Project is hosted via https://localhost:44340/ and the API is hosted via https://localhost:44349/. How can I host both projects together as I would it do with a JavaScript Framework?
You can either, depending on how you want to host and deploy your solution :
API and application in 2 different hosts
Enable CORS in the API project Startup class :
public void Configure(IApplicationBuilder app)
{
...
app.UseCors(configure =>
{
// configure here your CORS rule
}
...
}
All in one host
In your API project
add a package reference to Microsoft.AspNetCore.Components.WebAssembly.Server
Setup the blazor server in your Startup class
public void Configure(IApplicationBuilder app)
{
app.UseBlazorFrameworkFiles();
...
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToFile("index.html");
});
}
You can create a sample solution with : dotnet new blazorwasm --hosted. It'll create a solution with a Blazor wasm project and a host.
Docs
With the latest update to the templates dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview2.20160.5
You can create a Blazor WebAssembly app setup with authentication using ASP.NET Core Identity and IdentityServer by running the following command:
dotnet new blazorwasm --hosted --auth Individual -o BlazorAppWithAuth1
This creates:
Client Side Blazor
A single Project that can be used for MVC, API and razor pages, that contains an "inline" IdentityServer which can be used to secure the API calls
I was stuck on how to have IS4 in the same project as the APi (it's a small project and a independently hosted IDP would be overkill and I just want to deploy one thing) but this template shows how.
source: https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-2-release-now-available/

Authenticate with Azure AD using ASPNET Core 2 from behind Corporate Proxy

I have an ASPNET Core 2 application which I am trying to Authenticate with Azure AD using OpenId. I just have boilerplate code from selecting Single Organization Authentication in the ASPNET Core 2 templates, so no custom code. I followed the article here.
The app is not able to get metadata from the Azure AD application because of proxy. The same URL returns data if I just paste it in browser.
The error I get is:
HttpRequestException: Response status code does not indicate success: 407 (Proxy Authentication Required).
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
IOException: IDX10804: Unable to retrieve document from: 'https://login.microsoftonline.com/my-tenant-id/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever+d__8.MoveNext()
I have another ASPNET 4.5.2 application where I am able to perform authentication with the same Azure AD app as above after setting proxy in code like below:
System.Net.HttpWebRequest.DefaultWebProxy = new WebProxy
{
Address = new Uri("http://my-company-proxy:8080"),
Credentials = new NetworkCredential
{
UserName = "proxyusername",
Password = "proxypassword"
}
};
So Essentially my problem is to get past the Proxy Authentication in ASPNET Core 2.
I have tried Microsoft.AspNetCore.Proxy package. Its pretty much broken and doesn't work for me. Also I tried adding the Proxy entries in machine.config (which are actually not required for 4.5.2 app) but that doesn't work as well. I believe getting past a corporate proxy should be very trivial, but doesn't look like it so far.
Tratcher's comment pointed me in the right direction and I got it working, but just to help everyone with it, below is what you need to do:
builder.AddOpenIdConnect(options => options.BackchannelHttpHandler = new HttpClientHandler
{
UseProxy = true,
Proxy = new WebProxy
{
Credentials = new NetworkCredential
{
UserName = "myusername",
Password = "mypassword"
},
Address = new Uri("http://url:port")
}
});
In Full .net framework setting up a proxy is using a config setting
entry but to use an HTTP proxy in .net core ,you have to implement
IWebProxy interface.
Microsoft.AspNetCore.Proxy is proxy middleware which serves a different purpose (to setup reverse proxy) not as an http proxy .Refer this article for more details
To implement a webproxy in .net core,
public class MyHttpProxy : IWebProxy
{
public MyHttpProxy()
{
//here you can load it from your custom config settings
this.ProxyUri = new Uri(proxyUri);
}
public Uri ProxyUri { get; set; }
public ICredentials Credentials { get; set; }
public Uri GetProxy(Uri destination)
{
return this.ProxyUri;
}
public bool IsBypassed(Uri host)
{
//you can proxy all requests or implement bypass urls based on config settings
return false;
}
}
var config = new HttpClientHandler
{
UseProxy = true,
Proxy = new MyHttpProxy()
};
//then you can simply pass the config to HttpClient
var http = new HttpClient(config)
checkout https://msdn.microsoft.com/en-us/library/system.net.iwebproxy(v=vs.100).aspx

Hosting MVC 4 site with Google Drive Client API on AppHarbor hang on oAuth authentication

I am playing with Google Drive Client API with MVC 4 web project. The code works great locally with IIS express. However, when I deploy the site to AppHarbor, the oAuth authentication hang. I tried both web client credentials and installed app client credentials. What do I need to do to get it working?
Here is the code snippet for Authentication:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = { Client_ID set in Google developer console},
ClientSecret = { Client secret in Google developer console},
},
new[] { DriveService.Scope.Drive },
"user",
CancellationToken.None).Result;
//Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Google Drive Reader",
});
//More code goes here
return View();
}
}
Update:
I figured this out and put an answer to this question in case others may what to know.
I figured this out.
First of all, the method I used in the question works only for standalone applications, does not work for MVC applications. MVC application should follow the method documented here:
https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications
One important thing to notice is that web application client ID should be used, and the Redirect URL needs to be the URL to your AuthCallbackController.
Second, there is a problem in the Sample code: in HomeController
public async Task IndexAsync(CancellationToken cancellationToken)
Should be:
public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
Third: make sure adding the following appSetting to web.config so that AppHarbor sends correct redirect url.
<appSettings>
<add key="aspnet:UseHostHeaderForRequestUrl" value="true" />
</appSettings>
After that, it worked for me both locally with IIS Express and on AppHarbor.