Controller not working (except WeatherForecast) in .NET Core with React - asp.net-core

I created a fresh .NET Core with React.js project, which has this by default in Program.cs:
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html"); ;
The provided WeatherForecastController works, i.e. this works:
https://localhost:44449/weatherforecast
But if I copy WeatherForecastController.cs and name it MyNewController.cs like this, it won't work.
[ApiController]
[Route("[controller]")]
public class MyNewController : ControllerBase
{
...
https://localhost:44449/mynew returns the content of index.html.
What am I missing?

In setupProxy.js you have to add /mynew in the context:
const context = [
"/weatherforecast",
"/mynew",
];
module.exports = function(app) {
const appProxy = createProxyMiddleware(context, {
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
});
app.use(appProxy);
};
Otherwise, the framework will interpret the url /mynew as a route to a screen and will handle it as such.

Related

Prevent nuxt auth to send authorization header to external url

I want to send a POST request to an external API with axios in a nuxt projekt where I use the nuxt auth module.
When a user is authenticated axios seems to automatically add an authorization header (which is fine and often required for calls to my backend API). However, when doing calls to an external API the header might not be accepted and cause the call to fail.
Is there any way to specify for which URLs the auth header should be added or excluded?
Here are the configurations of the auth and axios module in my nuxt.config
// Axios module configuration
axios: {
baseURL: '//localhost:5000',
},
// Auth module configuration
auth: {
strategies: {
local: {
endpoints: {
login: { url: '/auth/login', method: 'post', propertyName: 'token' },
logout: { url: '/auth/logout', method: 'delete' },
user: { url: '/auth/user', method: 'get', propertyName: 'user' },
},
},
},
}
Some more background:
In my particular usecase I want to upload a file to an Amazon S3 bucket, so I create a presigned upload request and then want to upload the file directly into the bucket. This works perfectly fine as long as the user is not authenticated.
const { data } = await this.$axios.get('/store/upload-request', {
params: { type: imageFile.type },
})
const { url, fields } = data
const formData = new FormData()
for (const [field, value] of Object.entries(fields)) {
formData.append(field, value)
}
formData.append('file', imageFile)
await this.$axios.post(url, formData)
I tried to unset the Auth header via the request config:
const config = {
transformRequest: (data, headers) => {
delete headers.common.Authorization
}
}
await this.$axios.post(url, formData, config)
This seems to prevent all formData related headers to be added. Also setting any header in the config via the headers property or in the transformRequest function does not work, which again causes the call to the external API to fail obviously (The request will be sent without any of these specific headers).
As I'm working with the nuxt axios module I'm not sure how to add an interceptor to the axios instance as described here or here.
Any help or hints on where to find help is very much appreciated :)
Try the following
Solution 1, create a new axios instance in your plugins folder:
export default function ({ $axios }, inject) {
// Create a custom axios instance
const api = $axios.create({
headers: {
// headers you need
}
})
// Inject to context as $api
inject('api', api)
}
Declare this plugin in nuxt.config.js, then you can send your request :
this.$api.$put(...)
Solution 2, declare axios as a plugin in plugins/axios.js and set the hearders according to the request url:
export default function({ $axios, redirect, app }) {
const apiS3BaseUrl = // Your s3 base url here
$axios.onRequest(config => {
if (config.url.includes(apiS3BaseUrl) {
setToken(false)
// Or delete $axios.defaults.headers.common['Authorization']
} else {
// Your current axios config here
}
});
}
Declare this plugin in nuxt.config.js
Personally I use the first solution, it doesn't matter if someday the s3 url changes.
Here is the doc
You can pass the below configuration to nuxt-auth. Beware, those plugins are not related to the root configuration, but related to the nuxt-auth package.
nuxt.config.js
auth: {
redirect: {
login: '/login',
home: '/',
logout: '/login',
callback: false,
},
strategies: {
...
},
plugins: ['~/plugins/config-file-for-nuxt-auth.js'],
},
Then, create a plugin file that will serve as configuration for #nuxt/auth (you need to have #nuxt/axios installed of course.
PS: in this file, exampleBaseUrlForAxios is used as an example to set the variable for the axios calls while using #nuxt/auth.
config-file-for-nuxt-auth.js
export default ({ $axios, $config: { exampleBaseUrlForAxios } }) => {
$axios.defaults.baseURL = exampleBaseUrlForAxios
// I guess that any usual axios configuration can be done here
}
This is the recommended way of doing things as explained in this article. Basically, you can pass runtime variables to your project when you're using this. Hence, here we are passing a EXAMPLE_BASE_URL_FOR_AXIOS variable (located in .env) and renaming it to a name that we wish to use in our project.
nuxt.config.js
export default {
publicRuntimeConfig: {
exampleBaseUrlForAxios: process.env.EXAMPLE_BASE_URL_FOR_AXIOS,
}
}

ASP.NET Core 3.0 catch-all route unexpected behavior

Recently i encounter some unexpected issue
I'm using ASP.NET Core 3.0 and i defined two routes in StartUp.cs
StartUp.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "file",
pattern: "{controller=File}/folder/{*path}",
new { Action = "Folder" });
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=File}/{action=Index}/{filename}");
});
FileController.cs
public class FileController : Controller
{
public IActionResult Folder(string path)
{
return Ok(path);
}
public IActionResult Index(string filename)
{
return Ok(filename);
}
}
request to file/folder/abc/abc i expected to match first route
but the result was 404 not found
but if i changed order of route
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=File}/{action=Index}/{filename}");
endpoints.MapControllerRoute(
name: "file",
pattern: "{controller=File}/folder/{*path}",
new { Action = "Folder" });
});
It work!
My questing is why first version don't work if i defined {controller=File}/folder/{*path} on top
I thought it will check route table sequentially
In general we always put customer routers before default route. For this issue if we put non catch-all route before default route it will work as expect. However if we put catch-all route before default route you will see that you could not find any pages when we access via file/folder/abc/abc. This issue is not existed in asp.net core 3.0 but also in asp.net core 2.0 and MVC project. If you need to use catch-all routes, it is better to put them later in the route talbe. You could also read this article for more details: Multiple routes. It will help to fix unknown issues.

NET CORE - How to create Home Page in API?

Everytime my api is started, it's executed with LOCALHOST:PORT/api/values/. How to LOCALHOST:PORT/ with a static home page?
In your project, locate your launchSettings.json file. In visual studio you will need to expand Properties to find it from solution explorer or use Ctrl + T. This file contains an array of profiles. Each profile has a launchUrl field where you can mention your path as empty.
As of adding a content in the home page, you can always make a middleware as follows:
app.Use(async (context, _next) => {
if (string.IsNullOrEmpty(context.Request.Path.ToString())
|| context.Request.Path.ToString() == "/")
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("Web API is now running.");
}
else
await _next();
});
You can always have an action, But I would recommend using a middleware like the above.
possible duplicate of How to set start page in dotnet core web api?
I assume you mean having a default page when the user navigates to http://localhost instead of calling http://localhost/api/controller.
In .net core 2 it's fairy easy to do. You can use static files if you only want to show a simple static page by adding
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...other code here...
app.UseDefaultFiles(new DefaultFilesOptions { DefaultFileNames = new List<string> { "index.html" } });
app.UseDefaultFiles();
app.UseStaticFiles();
}
and making sure there is an index.html in the wwwroot folder.
or you could use routing in mvc
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
});
see answer by aslan at https://stackoverflow.com/a/40651363/3786363
oh and unless your server is mapped to port 80 you will probably need to call localhost:port not just localhost.
Are you looking for something like this?
$.ajax({
url: "/api/values/METHOD/?PARAM=0",
type: "GET",
dataType: "json",
cache: false,
statusCode: {
200: function (data) {
//Do stuff
}
}
});
Anything running within the solution context will start at the root.

How to ignore routes in ASP.NET Core?

Previously, one would add something like this to Global.aspx.cs, which is gone in .NET Core:
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
Here's what I currently have in my Startup.cs (for .NET Core):
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
The problem is that in MVC (pre-Core) routes was a RouteCollection and in .NET Core it's a Microsoft.AspNetCore.Routing.IRouteBuilder so IgnoreRoute is not a valid method.
You could write middleware for this.
public void Configure(IApplciationBuilder app) {
app.UseDefaultFiles();
// Make sure your middleware is before whatever handles
// the resource currently, be it MVC, static resources, etc.
app.UseMiddleware<IgnoreRouteMiddleware>();
app.UseStaticFiles();
app.UseMvc();
}
public class IgnoreRouteMiddleware {
private readonly RequestDelegate next;
// You can inject a dependency here that gives you access
// to your ignored route configuration.
public IgnoreRouteMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
if (context.Request.Path.HasValue &&
context.Request.Path.Value.Contains("favicon.ico")) {
context.Response.StatusCode = 404;
Console.WriteLine("Ignored!");
return;
}
await next.Invoke(context);
}
}
.NET Core 3.1
For .NET Core 3.1 with endpoint routing, this seems like the easiest way. You don't need to build a middleware just for this simple case.
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/favicon.ico", async (context) =>
{
context.Response.StatusCode = 404;
});
// more routing
});
The original question is about ignoring routes. If you do want to serve a favicon, you can add HTML markup in _Layout.cshtml like this. This technique allows more control over where the icon is served from.
<link rel="icon" href="#Url.Content("~/images/favicon.ico")" />
I am using both of these techniques in my code.
If you want to make a static file accessible without the routing condition, simply use the build-in StaticFiles Middleware.
Activate it with app.UseStaticFiles();
in Configure Method and put your static files in wwwroot directory.
They're availible on HOST/yourStaticFile
For more information, refer here
inside public void Configure
add
app.Map("/favicon.ico", delegate { });
Allow favicon requests to be parsed by the route handler, and keep your routes to a minimum. Avoid using middleware, this just adds additional complexity to your code and means all other requests must go through the middleware before the route handler, which is worse in terms of performance for busy websites. For websites that aren't busy you would just be wasting your time worrying about this.
See https://github.com/aspnet/Routing/issues/207
In ASP.NET Core, you can write a constrained catch-all route template. To do so, in your ASP.NET Core example, replace the call to routes.MapSpaFallbackRoute with the following:
// Returns the home/index page for unknown files, except for
// favicon.ico, in which case a 404 error is returned.
routes.MapRoute(
name: "spa-fallback",
template: "{*url:regex(^(?!favicon.ico).*$)}",
defaults: new { Controller = "Home", action = "Index" });

Area routing breaks in MVC6

In previous versions of ASP.NET, and using MVC 5, I could set up a route like this in my AreaRegistraion:
context.MapRoute(
"pfrecent",
"Forums/Recent/{page}",
new { controller = ForumController.Name, action = "Recent", page = 1 },
new[] { "PopForums.Controllers" });
This would route /Forums/Recent to the forum controller and its recent action. However, I can't figure out how to make it work in ASP.Net 5/MVC 6. I've added [Area("Forums")] to the controller class, and used this route from the Startup class:
routes.MapRoute(
"pfrecent",
"Forums/Recent/{page}",
new { controller = ForumController.Name, action = "Recent", page = 1 });
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
However, it resolves to /Forums/Forum/Recent?page=1. The intention is to continue using /Forums/Recent.
We are using this for enabling areas in MVC 6:
// Configure MVC routing
app.UseMvc(routes =>
{
// Areas support
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
// Default routing
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
The first route is for areas, second is for main content.
On Startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "Business",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
})
on Controller
[Area("Business")]
public class DemoController : Controller
{
public IActionResult Index()
{
return View();
}
}
Place this in your Startup.cs file at the TOP of the routes list:
routes.MapRoute(
name: "forumsAreaRoute",
template: "Forums/{action}/{page?}",
defaults: new {area = "Forums", controller = "Forum", action = "Recent", page = 1});
Your Forum controller should look like this:
[Area("Forums")]
public class ForumController : Controller
{
// GET: /<controller>/
public IActionResult Recent(int? page)
{
// Do action stuff here
}
}
This solution will satisfy a url of http://somedomain.com/Forums/Recent/1 and return the first page.
I hate answering my own questions, but after getting back to this, and looking at source and experimenting, I found that you have to specify the area in the route mapping, as well as the HtmlHelpers (I'm assuming this will be true for tag helpers as well, but I haven't gone that far yet in conversion.) So the route mapping has to look like this:
routes.MapRoute(
"pfrecent",
"Forums/Recent/{page?}",
new { controller = ForumController.Name, action = "Recent", page = 1, Area = "Forums" }
);
The important part is the Area property in the route value object. The HtmlHelper has to look like this, also naming the Area:
#Html.ActionLink(PopForums.Resources.Recent, "Recent", ForumController.Name, new { page = 1, Area = "Forums" }, null)
It causes the URL for the recent page to be /Forums/Recent as expected. As best I can tell, setting routes in a derived AreaRegistration class in MVC5 set the area value on the routes for you. Having [Area("Forums")] on your controller class (which I already had) seems to assume the previous role of area registration.