Asp.net core web api custom error response for unmatched controller action? - asp.net-core

I have an issue with ASP.NET Core Web API, where I want to display a custom error message model if non of the controller actions were matched.
So, it would be a 404 error but with a customized body.
I haven't tried anything because I am not sure if this is even possible.

You can add a catch-all route by adding an action method with a route that will always match if no other does.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "CatchAll",
template: "{*url}",
defaults: new { controller = "CatchAll", action = "Index" });
});
For Web API:
public class CatchAllController : Controller
{
[HttpGet("{*url}", Order = int.MaxValue)]
public IActionResult Index()
{
return NotFound(YourModel)
}
}
For MVC:
public class CatchAllController : Controller
{
public IActionResult Index()
{
Response.StatusCode = StatusCodes.Status404NotFound;
return View();
}
}

app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500;
});
});
Using this middleware you can actually catch the code and then handle it accordingly. It's taken from this page
. There are various other examples in the link some simpler ones might fit you better

Related

.netCore 5 Area route Not working in RedirectToActionArea and go as a query string

when I use below command In asp.net core 5
return RedirectToAction("Index", "SystemUsersManagement", new { area = "admin" });
it not Works properly and get Status Code: 404; Not Found Error. and Area go as a query string like
https://localhost:44322/SystemUsersManagement/Index?area=admin
where is wrong.
my controller :
[Area("Admin")]
public class SystemUsersManagementController : Controller
{
public ActionResult Save()
{
//do something
return RedirectToAction("Index", "SystemUsersManagement", new { area = "admin" });
}
}
My route in startup:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
routes.MapRoute(
name: "Default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Login" }
);
routes.MapRoute("DefaultApiWithId", "Api/{controller}/{id?}");
routes.MapRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" });
routes.MapRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" });
});
also in ConfigureService
.
.
services.AddMvc(option=>option.EnableEndpointRouting=false).AddJsonOptions(options => {
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
Be sure add [Area] attribute in your controller like below:
[Area("Admin")]
public class SystemUsersManagementController : Controller
{
public ActionResult Index()
{
return View();
}
}
The Project structure should be like below:

ASP.NET Core 3.0 Redirect HTTP 4XX and 5XX requests to customized error pages while keeping the error code

I'm looking to redirect HTTP requests with 4XX or 5XX error code to a custom error page, while keeping the error code at the request level. I also want to redirect exceptions to a custom error page, with an error code 500.
For that I used in my Startup file
"app.UseStatusCodePagesWithReExecute("/error/{0}");
app.UseExceptionHandler("/error/500");"
associated with an Error controller.
The part about exceptions works well.
I also manage to redirect non-existent routes to my custom page while keeping the 404 error.
However, I can't redirect the following actions to my custom error pages:
return NotFound()
return BadRequest()
return StatusCode(404)
What would be the technical solution applied to accomplish this?
Here is the Configure function of my Startup file :
app.UseStatusCodePagesWithReExecute("/error/{0}");
app.UseExceptionHandler("/error/500");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "Error-StatusCode-Route",
pattern: "error/{statusCode}",
defaults: new { controller = "Error", action = "InternalServerError" }
);
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
You could custom middleware to deal with it:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStatusCodePagesWithReExecute("/error/{0}");
app.UseExceptionHandler("/error/500");
app.Use(async (context, next) =>
{
await next();
var code = context.Response.StatusCode;
var newPath = new PathString("/error/"+code);
var originalPath = context.Request.Path;
var originalQueryString = context.Request.QueryString;
context.Features.Set<IStatusCodeReExecuteFeature>(new StatusCodeReExecuteFeature()
{
OriginalPathBase = context.Request.PathBase.Value,
OriginalPath = originalPath.Value,
OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null,
});
// An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
// the endpoint and route values to ensure things are re-calculated.
context.SetEndpoint(endpoint: null);
var routeValuesFeature = context.Features.Get<IRouteValuesFeature>();
routeValuesFeature?.RouteValues?.Clear();
context.Request.Path = newPath;
try
{
await next();
}
finally
{
context.Request.QueryString = originalQueryString;
context.Request.Path = originalPath;
context.Features.Set<IStatusCodeReExecuteFeature>(null);
}
});
app.UseHttpsRedirection();
app.UseStaticFiles();
//...
}
For your ErrorController:
public class ErrorController : Controller
{
// GET: /<controller>/
public IActionResult InternalServerError()
{
return View();
}
[Route("error/404")]
public IActionResult StatusCode404()
{
//redirect to the StatusCode404.cshtml
return View();
}
[Route("error/400")]
public IActionResult StatusCode400()
{
return View();
}
}
If you are using core3, then this is a known bug. This bug will be fixed in 3.1.
Here is a link to the issue: https://github.com/aspnet/AspNetCore/issues/13715
For now there is a workaround. You can add this code right after you call app.UseStatusCodePagesWithReExecute("/error/{0}");
app.Use((context, next) =>
{
context.SetEndpoint(null);
return next();
});
This will render your custom pages when you return NotFound or BadRequest from your controller action.

Why can't I use [HttpPost] instead [HttpPost("[action]")]?

I don't understand why I need to use [HttpPost("[action]")] instead of [HttpPost]?
If use [HttpPost("[action]")] then the request from angular client hits the controller action.
// POST: api/LeaveRequests
[HttpPost("[action]")]
public async Task<IActionResult> PostLeaveRequest([FromBody] LeaveRequest leaveRequest)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.LeaveRequests.Add(leaveRequest);
await _context.SaveChangesAsync();
return CreatedAtAction("GetLeaveRequest", new { id = leaveRequest.EmployeeNo }, leaveRequest);
}
But
If I only put [HttPost] then the request from anglular client does not hit controller action and instead it hits method with the path [HttpGet("{id}")]
My MVC route
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
my angular http.post
return this.http.post(this._apiUrl, body, options)
.map(res => res.json()) // ...and calling .json() on the response to return data
.catch((error: any) => Observable.throw(error.json().error || 'Server error'))
.subscribe();
This [HttpPost("blah blah")] is for defining route link
Alternatively you can use Attribute routing Read this article
[HttpPost]
[Route("PostLeaveRequest")]
public async Task<IActionResult> PostLeaveRequest([FromBody] LeaveRequest leaveRequest)
{
..................................
}
I had this issue in dotnet core and as above - it is used in routing templates with attribute replacement.
Link here

asp.net mvc4 VaryByParam does not work

My code is as below:
[HttpGet]
[OutputCache(Duration = 90, VaryByParam = "cityCode")]
public ActionResult About(string userName, string cityCode)
{
//do something...
return View();
}
the cache works fine when I access the URL:
http://localhost:52121/LabOne/MvcCache/About?userName=admin&cityCode=010
but when I access this route URL as below, the cache does not work, why?
http://localhost:52121/LabOne/MvcCache/About/admin/010
I copied your code and I tested it on my machine and I configured the RouteConfig as the following
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "aboutRoute",
url: "{controller}/{action}/{userName}/{cityCode}",
defaults: new { controller = "Home", action = "About", userName = UrlParameter.Optional, cityCode = UrlParameter.Optional }
);
}
}
And I faced the same problem, I will explain this :
The OutputCache is depending on the URL, and the examples you provides is really two different URLs, although they are will make the same result.
So try to request the URL http://localhost:52121/LabOne/MvcCache/About/admin/010 one more time. and you will see that OutputCache is working, and the MVC will bring the result from cache, because the OutputCache has been cached this URL on previous time.
UPDATE
according to this question Using outputcache in MVC and its accepted answer, the Caching are working with the URL and has no relevance with the MVC Routing System.

Can't remove controller name MVC4

I'm worknig in MVC4 where i need to redirect a user under a certain scenario to LogOut. I'm using the redirect in the .js file. But when i try to redirect the routing is not proper. It adds to the existing controller and throw me error.
//Code
// .cshtml:
<script type="text/javascript">
var logoutUrl = '#Url.Action("LogOut", "Account")';
</script>
//Controller:
[AllowAnonymous]
public ActionResult LogOut()
{
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account", null);
}
//.JS:
window.location.href = logoutUrl;
When i try to redirect the user will be in page
/Home/Index
But, after redirecting it goes to
/Home/Account/LogOut
instead of /Account/LogOut
My routeConfig,
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
}
this throws me the error. But i need to remove the Home from the url and redirect to Account/LogOut.
How can i achieve this?
Please make sure you are getting the correct url or for testing purpose just use hard-coded url
and try once
var logoutUrl = '/Account/LogOut';
PS: i would have commented if i could but less reputation