No file provider has been configured to process the supplied file - .net-core

I am using Single Page Application with .Net core 2.2. I have following in my startup.cs.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa - fallback",
defaults: new { controller = "CatchAll", action = "Index" });
});
But having following
public class CatchAllController : Controller
{
public IActionResult Index()
{
return File("~/index.html", "text/html");
}
}
gives me following error.
No file provider has been configured to process the supplied file
I was following following article (one difference. article uses API project. I had taken angular project).
https://www.richard-banks.org/2018/11/securing-vue-with-identityserver-part1.html

I was just fighting this exact problem. Even though the File() method claims to take a "virtual path" I could never get it to load the file without the error you're getting. Here's what I ended up piecing together after reading a number of different posts.
[Route("[controller]")]
public class DocumentationController : ControllerBase
{
private readonly IHostingEnvironment _env;
public DocumentationController(IHostingEnvironment env)
{
_env = env;
}
[HttpGet, Route("apiReference")]
public IActionResult ApiReference()
{
var filePath = Path.Combine(_env.ContentRootPath,
"Controllers/Documentation/My Document.pdf");
return PhysicalFile(filePath, "application/pdf");
}
}

Related

Why A simple method having two arguments is not working in ASP.NET MVC?

My controller:
public class AjaxController : Controller
{
private readonly IGenerationUnitMobileService _generationUnitMobileService;
public AjaxController(IGenerationUnitMobileService generationUnitMobileService)
{
_generationUnitMobileService = generationUnitMobileService;
}
public IActionResult MobileExistToAnotherGenerationUnit(String mobile, long generation_unit_id)
{
//ViewBag.Result = _generationUnitMobileService.MobileExistToAnotherGenerationUnit(mobile,generationUnitId);
return View();
}
}
And My view file is very simple:
#model PgcgSms.WebSite.Models.GenerationUnitMobileViewModel
#{
Layout = "~/Views/Shared/_Ajax.cshtml";
}
#ViewBag.Result
This is so much straight forward. But when I browse at: http://localhost:57216/Ajax/MobileExistToAnotherGenerationUnit/01719393045/1
I am getting the following error message:
This localhost page can’t be found
No webpage was found for the web address: http://localhost:57216/Ajax/MobileExistToAnotherGenerationUnit/01719393045/1
Search Google for localhost 57216 Ajax Mobile Exist To Another Generation Unit 01719393045
HTTP ERROR 404
I checked the view file and spellings several times. Whats wrong with my code?
In the RouteConfig.cs file you will have to specify the default route.
For Example:
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 = "AjaxController", action = "MobileExistToAnotherGenerationUnit", id = UrlParameter.Optional }
);
}
}

ASP.Net Web API 404 Error on 2nd API

So I already have 1 Web API set up and working great, but now that I am trying to set up my own admin panel ( which I did ), I need to use the DeleteUser() function from the Web API named AdminApi but I can't seem to get it working. I keep getting 404 error while giving the path that the API should be at.
Web Api Config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Global :
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Create the custom role and user
RoleActions roleActions = new RoleActions();
roleActions.AddUserAndRole();
}
AdminApi :
[Authorize(Roles = "admin")]
public class AdminApiController : ApiController
{
public string test()
{
return "test";
}
[HttpPost]
public string DeleteUser(string id)
{
ApplicationDbContext db = new ApplicationDbContext();
var user = db.Users.Find(id);
if (user != null)
{
string email = user.Email;
db.Users.Remove(user);
return "Succesfully deleted user : " + email;
}
else
return "Failed to delete user.";
}
}
Ajax :
function deleteUser (id)
{
$.ajax({
url: '../api/AdminApi/DeleteUser',
type: 'POST',
contentType: "application/json",
dataType: 'json',
data: JSON.stringify(id),
success: function (data) {
alert(data);
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
}
The ajax function is called on the page /Admin/AdminPage
so to get to the web api -> ../api/AdminApi
and the function to delete users is DeleteUser
-> ../api/AdminApi/DeleterUser
I don't get why I get a 404 error. I can understand if my function DeleteUser is not working since I haven't tested it, but I can't test it if I can't get in the function.
The issue is related to how you use the attribute:
[Route("DeleteUser")]
If you use the Attribute Route. at Method level what it does is to define new route or more routes therefore the way you should use it is like [Route('Url/path1/route1')]:
As an example of how it works:
//GET api/customer/GetMetaData
[Route('/api/customer/GetMetaData')]
public string Get2(){
//your code goes here
}
If you will be declaring several Routes in your class then you can use RoutePrefix attribute like [RoutePrefix('url')] at class level. This will set a new base URL for all methods your in Controller class.
For example:
[RoutePrefix("api2/some")]
public class SomeController : ApiController
{
// GET api2/some
[Route("")]
public IEnumerable<Some> Get() { ... }
// POST api2/some/DeleteUser/5
[HttpPost]
[Route("DeleteUser/{id:int}")]
public Some DeleteUser(int id) { ... }
}
Update
By default Web API looks at the routing URL first, what is in your [Route] I mean and it tries to match it against your post. However if your method has a complex object as parameter WebApi can't get the values from the request URI because the parameter is a complex type Web API uses a media-type formatter to read the value from the request body.
Since your string id is not a complex object and it is part of your Route WebApi expects it as part of your URL not the body. Try this instead:
[HttpPost]
public string DeleteUser([FromBody]string anotherName)

Working with WebApi in a Web Forms application

I am trying to add WebApi in my Web Forms application (Visual Studio 2015, .NET 4.6). I added App_Start folder and WebApiConfig.cs in it as following (pretty much copied from an MVC app):
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Adding routes for WebApi controllers
RouteTable.Routes.MapHttpRoute(
name: "SearchApi",
routeTemplate: "api/search",
defaults: new { controller = "SearchController" }
);
}
}
Then, I created a folder Controllers/WebApi and added SearchController.cs:
namespace IdeaMaverick.Web.Controllers.WebApi
{
public class SearchController : ApiController
{
public IEnumerable<string> Get()
{
return new [] { "value1", "value2" };
}
}
}
But when I hit http://example.com/api/search in the browser, I get this error:
{"message":"No HTTP resource was found that matches the request URI
'http://www.example.com/api/search'.","messageDetail":"No type
was found that matches the controller named 'SearchController'."}
I'm obviously missing something but I can't figure out what.
I found the issue - in defaults for the route I had to specify the controller name omitting "Controller", so it has to be like
defaults: new { controller = "Search" }

Routes mapping in MVC 5

I'm trying to understand how the route config works in the MVC 5.
I have the following structure for my application:
BaseCRUDController
public class BaseCRUDController<TEntity, TEntityViewModel> : Controller
where TEntity : class
where TEntityViewModel : class
{
private readonly IBaseService<TEntity> baseService;
public BaseCRUDController(IBaseService<TEntity> service)
{
this.baseService = service;
}
[HttpGet]
public virtual ActionResult Index()
{
IList<TEntityViewModel> entities = baseService.FindFirst(10).To<IList<TEntityViewModel>>();
return View(entities);
}
}
CountriesController
[RouteArea("management")]
[RoutePrefix("countries")]
public class CountriesController : BaseCRUDController<Country, CountryViewModel>
{
private readonly ICountryService service;
public CountriesController(ICountryService service)
: base(service)
{
this.service = service;
}
}
What I'm trying to do is simple: http://myapplication.com/management/countries.
I have many others controllers that the superclass is the base controller. I'm doing this way to avoid code repetition, since that the controllers have similar structure.
The problems are:
I can't reach the url that I want (/management/countries)
I don't know how to configure my Home Controller, because I want that it could be reached by http://myapplication.com
How could I fix these problems?
My route config is like that:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
After a little playing around I managed to get it to work.
The problem is when you are using attribute routing there are no default route parameters assigned.
For example in your Default route the defaults are controller = "Home", action = "Index". So if you were to call http://myapplication.com/ the routing engine will automatically default to /Home/Index/ and so on.
However the attribute route has no way of knowing you want to default to the Index action (or any other action for that matter).
To solve the issue add the Route attribute to the CountriesController like this:
[RouteArea("management")]
[RoutePrefix("countries")]
[Route("{action=Index}")] // this defines the default action as Index
public class CountriesController : BaseCRUDController<Country, CountryViewModel>
{
private readonly ICountryService service;
public CountriesController(ICountryService service)
: base(service)
{
this.service = service;
}
}
Also for future reference Phil Haack's Route Debugger is extremely helpful for figuring out routing issues.
Have you defined Area (management) for your controllers?
if you try without Area, is that getting redirect properly?

Abstract Generic ODataController Class Leads To 'No HTTP resource was found'

I am trying to abstract the auto-generated ODataController class in VS 2013 because the code looks identical across different controllers except the name of the POCO, so, I did the following:
public abstract class ODataControllerBase<T,DB> : ODataController
where T : class, IIdentifiable, new()
where DB : DbContext, new()
{
protected DB _DataContext;
public ODataControllerBase() : base()
{
_DataContext = new DB();
}
// only one function shown for brevity
[Queryable]
public SingleResult<T> GetEntity([FromODataUri] int key)
{
return SingleResult.Create(_DataContext.Set<T>().Where(Entity => Entity.Id.Equals(key)));
}
}
IIdentifiable is an interface that forces the T parameter to have a readable/writable Id integer property.
The implementation looks like this (POCOs and DataContexts should've already been created)
public class MyObjectsController : ODataControllerBase<MyObject,MyDbContext>
{
public MyObjectsController() : base()
{
}
// That's it - done because all the repetitive code has been abstracted.
}
Now, my WebApiConfig's Register function contains the following only:
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyObject>("MyObjects");
config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
}
I run the project, http://localhost:10000/odata/MyObjects and I get the response:
<m:error>
<m:code/>
<m:message xml:lang="en-US">No HTTP resource was found that
matches the request URI `http://localhost:10000/odata/MyObjects.`
</m:message>
<m:innererror>
<m:message>No routing convention was found to select an action
for the OData path with template '~/entityset'.
</m:message>
<m:type/>
<m:stacktrace/>
</m:innererror>
</m:error>
What is missing? What should I remove? Is this something we can't do, i.e. are we really required to inherit ODataController directly with no intermediate parent class?
In one of our projects We also use a generic ODataController base class where we actually use GetEntity for retrieving single entities and GetEntitySet for retrieving a list of entities.
According to your supplied URL and the resulting error message, the ODATA framework cannot find an ODataAction for ~/entityset. As you have given http://localhost:10000/odata/MyObjects as the example, the action in question cannot be public SingleResult<T> GetEntity([FromODataUri] int key) as this only corresponds to a query like this http://localhost:10000/odata/MyObjects(42).
Our code for a generic controller looks like this:
public abstract class OdataControllerBase<T> : ODataController
where T : class, IIdentifiable, new()
{
protected OdataControllerBase(/* ... */)
: base()
{
// ...
}
public virtual IHttpActionResult GetEntity([FromODataUri] long key, ODataQueryOptions<T> queryOptions)
{
// ...
return Ok(default(T));
}
public virtual async Task<IHttpActionResult> GetEntitySet(ODataQueryOptions<T> queryOptions)
{
// ...
return Ok<IEnumerable<T>>(default(List<T>));
}
public virtual IHttpActionResult Put([FromODataUri] long key, T modifiedEntity)
{
// ...
return Updated(default(T));
}
public virtual IHttpActionResult Post(T entityToBeCreated)
{
// ...
return Created(default(T));
}
[AcceptVerbs(HTTP_METHOD_PATCH, HTTP_METHOD_MERGE)]
public virtual IHttpActionResult Patch([FromODataUri] long key, Delta<T> delta)
{
// ...
return Updated(default(T));
}
public virtual IHttpActionResult Delete([FromODataUri] long key)
{
// ...
return Updated(default(T));
}
}
The code for a specific controller then is as short as this:
public partial class KeyNameValuesController : OdataControllerBase<T>
{
public KeyNameValuesController(/* ... */)
: base()
{
// there is nothing to be done here
}
}
However we found out that both Get methods (for single result and enumerable result) actually have to start with Get. First we tried List instead of GetEntitySet and this did not work, as the framework then expects a POST for the List action).
You can actually verify and diagnose the resolving process by supplying a custom IHttpActionSelector as described in Routing and Action Selection in ASP.NET Web API (ahving a look at ASP.NET WEB API 2: HTTP Message Lifecycle might also be worth it).
So actually it is possible to use GetEntity as your method name as you originally tried in your example and there is no need to rename it to simple Get. In addition, there is no need for any modification in your ODATA configuration.
To determine which action to invoke, the framework uses a routing table. The Visual Studio project template for Web API creates a default route:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Routing by Action Name
With the default routing template, Web API uses the HTTP method to select the action. However, you can also create a route where the action name is included in the URI:
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I configured config as follows:
config.Routes.MapHttpRoute(
name: "GetMessage",
routeTemplate: "api/{controller}/{action}/{quoteName}",
defaults: new { quoteName = RouterParameters.Optional }
);
Access your URI like this:
http://localhost:42201/api/Extract/GetMessage/Q3
OR
http://localhost:42201/api/Extract/GetMessage/?quotename=Q3

Resources