Http route without parameters and a point inside the route - asp.net

I'm currently creating a webservice using ApiController classes. Due to some restrictions I have to put a Point in the route itself (terminal.journalentry). And use this as the base URL. Thus:
http://localhost:59684/terminal.journalentry
as example for a call. Now though I've run into Problems.
When I use the following code with this call: http://localhost:59684/terminal.journalentry it works without a hitch.
public class JournalController : ApiController
{
[HttpGet]
[Route("terminal.journalentry/{id}")]
public void WriteJournalEntry(int id)
{
}
}
Though I Need to use the method without any Parameters involved. But when I try the following:
public class JournalController : ApiController
{
[HttpGet]
[Route("terminal.journalentry")]
public void WriteJournalEntry()
{
}
}
With the call being: http://localhost:59684/terminal.journalentry
I get:
HTTP Error 404.0 - Not Found
Now my question is: What is wrong there and what Needs to be done in order to use the above URL without running into Errors?
Edit as it was asked what the Content of Routes.config is:
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 }
);
}
}
WebApiConfig.cs has only the following Content inside Register: config.MapHttpAttributeRoutes();

I think you need to configure a handler to ignore the ".journalentry".
Usually anything with a fullstop is considered a file, and I imagine this is happening here.
In the web.config for your API, try and find the following section:
<system.webServer>
<handlers>
And add a handler definition:
<add name="JournalEntryExtensionHandler" path="*.journalentry*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
You might need to play around a bit with the path wildcard settings.
When I've done this previously the URL ended with a filename so the path was "*.jpg" for example.

Related

Asp.net web API CORS series/mysterious issue

I am facing a CORS policy problem and I do not know how to fix it. I tried many approaches but what makes me crazy is that the service works fine on some devices and I can utilize all its resources and works a little bit on others and does not work at others while the whole devices having the same configuration equally set. To be more clear I am having a Web application based entirely and only on AngularJS 2 and a Web API that exposes a few actions. I installed the IIS and hosted the Web API on the default Web Site, which means it can be called from port 80 for simplicity, and I hosted the Web application using port 4200. Now let's give more detail about my Web API application structure.
EMG.WebApi.Core -> this project is the core project in which I put the controller classes and the web configuration class
EMG.WebApi.WebHost -> this project is used merely for hosting and it contains a reference to the EMG.WebApi.Core project and is the one that contains the Global.asax and within its Application_Start I am calling the Register method of Configuration class of the WebApi.Core and give it as a parameter the GlobalConfiguration object to register my handlers, tracers etc.
using EMG.ElectrometerWS.WebApi.Core;
using System;
using System.Web.Http;
using EMG.ElectrometerWS.WebApi.Core.Configuration;
namespace EMG.ElectrometerWS.WebApi.WebHost
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
GlobalConfiguration.Configuration.EnsureInitialized();
}
}
}
using EMG.ElectrometerWS.WebApi.Core.Handlers;
using EMG.ElectrometerWS.WebApi.Core.Tracer;
using System;
using System.Configuration;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Http.Tracing;
namespace EMG.ElectrometerWS.WebApi.Core.Configuration
{
public static class WebApiConfig
{
...
public static string CorsOrigin
{
get
{
string result =
ConfigurationManager.AppSettings.Get("CorsOrigin");
if (!string.IsNullOrEmpty(result))
{
return result;
}
throw new Exception("AppSetting CorsOrigin not found in
web.config file.");
}
}
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
EnableCorsAttribute enableCors =
new EnableCorsAttribute(CorsOrigin, "*", "*");
config.EnableCors(enableCors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//config.Routes.MapHttpRoute(
// name: "Secret Api",
// routeTemplate: "secretapi/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional },
// constraints: null,
// handler: new ApiKeyProtectionMessageHandler() {
// InnerHandler = new HttpControllerDispatcher(config)
// });
// Enable ASP.NET Web API tracing
//config.EnableSystemDiagnosticsTracing();
//config.Services.Replace(typeof(ITraceWriter), new
// EntryExitTracer());
//config.Services.Replace(typeof(ITraceWriter), new WebApiTracer());
//config.MessageHandlers.Add(new EmptyPostBodyMessageHandler());
// Message handler to check the performance in production
environment ONLY
config.MessageHandlers.Add(new TracingHandler());
//config.MessageHandlers.Add(new XHttpMethodOverrideHandler());
config.MessageHandlers.Add(new JwtHandler());
}
}
}
EMG.ElectrometerWS.WebApi.WebHost Web.Cofig
<appSettings>
....
<add key="CorsOrigin" value="http://localhost:4200"/>
</appSettings>
What makes me crazy is that everything works fine on my colleague laptop and he can use all the actions. On mine I cannot call some of PUT methods while I can for others on other colleague/testers they can only call GET methods!!! And increases my surprises is that after clearing the browser history/cookies one of those laptops that have only GET methods being called have all things works fine.
What I have tried:
First I added the below code as you can notice above to the configuration class
EnableCorsAttribute enableCors =
new EnableCorsAttribute(CorsOrigin, "*", "*");
config.EnableCors(enableCors);
Creating the following handler and registered it as the first handler before other handlers
public class CorsPreflightHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
request,
CancellationToken cancellationToken)
{
if (request.Headers.Contains("Origin") && request.Method == HttpMethod.Options)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "*");
return response;
}
return await base.SendAsync(request, cancellationToken);
}
}
Removing the previous code and configured the CORS using the Web.config file of the WebHost project
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http:localhost:4200" />
<add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Headers" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
Finally removing the web config tags and enabled if on each controller class
[EnableCors(origins: "http://localhost:4200", headers: "*", methods: "*")]
public class XXXController : ApiController
{
Public string Get(int id)
{
return "value";
}
}
The CORS issue seems solved in meantime. I used the first approach by setting an application setting key/value for the allowed Origin then use that key/value to retrieve and register that Origin through a CORS attribute.
<appSettings>
....
<add key="CorsOrigin" value="http://localhost:4200"/>
</appSettings>
public static string CorsOrigin
{
get
{
string result =
ConfigurationManager.AppSettings.Get("CorsOrigin");
if (!string.IsNullOrEmpty(result))
{
return result;
}
throw new Exception("AppSetting CorsOrigin not found in web.config file.");
}
}
public static void Register(HttpConfiguration config)
{
....
EnableCorsAttribute enableCors =
new EnableCorsAttribute(CorsOrigin, "*", "*");
config.EnableCors(enableCors);
}
However, I still do not know what cause the problem from the beginning it may be an IIS issue or missing feature etc.

Trying to display Custom View in MVC app but it says requested URL not found

I am new to azure, MVC and also ASP.NET. I am writing MVC Cloud service with ASP.NET web role. Please help me with this problem
When I create the application there are default views but I wanted to see my view so I set my view as start page. I also changed the values in RegisterRoutes method
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "User", action = "AddUser", id = UrlParameter.Optional }
);
}
When I run the app, it gives HTTP 404 error because it could not find request URL : /Views/User/AddUser.cshtml
In MVC you don't put the view in the URL to get it rendered.
This won't work: /Views/User/AddUser.cshtml
As you've correctly put in your question the default route is {controller}/{action}/{id} with id being optional.
So assuming that User is your controller, i.e. you have a class called UserController, which looks something like:
namespace My.Controllers
{
public class UserController : Controller
{
which has an action on it called AddUser:
public ActionResult AddUser()
{
// implementation logic
return View();
}
Then the default route will display your view when it processes the URL /User/AddUser
In MVC 5, this looks something like:

Angularjs cant find view, tried everything I know

I have made a project using ng.Net template from visual studio, i got it up and running, i added a new ProgramController.cs, Program.cshtml template, programCtrl.js angular controller, and a program angular module.
And it just will not work.
I have a ASP.NET web api and angularjs on clientside.
Here are 2 example routes:
$routeProvider.when('/todomanager', {
templateUrl: 'App/TodoManager',
controller: 'todoManagerCtrl'
});
$routeProvider.when('/program', {
templateUrl: 'App/Program',
controller: 'programCtrl'
});
And what they do backend:
[HttpGet]
[Authorize]
public List<Program> GetPrograms()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.Programs;
//return db.Programs;
}
[HttpGet]
[Authorize]
public List<todoItem> GetUserTodoItems()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.todoItems;
}
The first one works, the second one gives tpload compile error (could not find template view)
I can get the TodoManager view if I call it in /program
But I cant reach my program.cshtml. It's in the same folder as TodoManager.cshtml
I could provide more code, But I dont know which, Because it all works.
My closest guess so far, is that I dont have access to that view, I'm being blocked by a blockviewhandler or the view doesnt exist.
I have the access.
If it was the viewblockhandler it would also block TodoManager.cshtml
And it exists xD I'm looking at it..
It's like i'm spamming 4 + 4 on a calculator and it keeps returning 5..
Please, anything, been stuck for 2 workdays.
EDIT additional code
//RouteConfig RegisterRoutes
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "App",
url: "{url}",
defaults: new { controller = "Main", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Main", action = "Index", id = UrlParameter.Optional }
);
}
//WebApiConfig Register
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
//I chaned the routeTemplate so that methods/services would be identified by their action, and not by their parameters.
//I was getting conflicts if I had more than one GET services, that had identical parameter options, but totally different return data.
//Adding the action to the routeTemplte correct this issue.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}", //routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
//blockviewhandler in Web.config
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*." verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
Edit: Project structure, i have a HomeController that returns my Index.cshtml in there, i have my ng-view which is where my views should be rendered.
Many seem to misunderstand my real question, i want to know:
How is it, that the todoManager works, and the program does not.
When using ASP.NET MVC, Web API etc. you should add "every" view to the mvc controller, you probably have something like this in one of your controllers:
public ActionResult TodoManager()
{
return PartialView();
}
assuming you are following a tutorial or modifying a sample it is probably in Controllers/AppController. If true you must add your another view as:
public ActionResult Program()
{
return PartialView();
}

No type was found that matches the controller named 'help'

I have been following this guide to add a help page to document my Web API project. My Controller is named HelpController and I have a route that I am trying to use to map the Index action to /Help. This is the only MVC controller in the project. Because the rest are Web API controllers, we removed the "/api" prefix from the default route in WebAPIConfig.cs.
The HelpController:
public class HelpController : Controller
{
public ActionResult Index()
{
var apiExplorer = GlobalConfiguration.Configuration.Services.GetApiExplorer();
return View(apiExplorer);
}
}
And route config:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "help",
defaults: new { controller = "Help", action = "Index"});
}
}
In Global.asax.cs
protected void Application_Start()
{
// ..
WebApiConfig.Register(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// ..
}
But when I try to navigate to /help in the browser I get the following error message.
<Error>
<Message>No HTTP resource was found that matches the request URI 'http://localhost/ws/help'.</Message>
<MessageDetail>No type was found that matches the controller named 'help'.</MessageDetail>
</Error>
EDIT: The message contains /ws/help as the application is hosted at localhost/ws in IIS.
Does anyone know what could be causing ASP.NET to not find my HelpController?
UPDATE: If I change the order of RouteConfig and WebApiConfig registration calls in Application_Start I get a 404 instead.
protected void Application_Start()
{
// ..
RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register(GlobalConfiguration.Configuration);
// ..
}
The request for help is being matched by Web API's route as you have removed api from its route template. if a request matches a route, further probing is not done on rest of the routes.
You probably have the default order in Global.asax where Web API routes are registered first and then the MVC routes. Could you share how your Global.asax looks like?
EDIT:
Based on your last comment, if you install HelpPage nuget package, make sure that the order in your Global.asax looks like this:
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
I had this same error today.
<Error>
<Message>No HTTP resource was found that matches the request URI 'xxx'.</Message>
<MessageDetail>No type was found that matches the controller named 'xxx'.</MessageDetail>
</Error>
In my case after 2 days of debuging I finally solved it by setting the microsoft report viewer dll to "Copy to local". Makes no sense to me how it could be related, but maby this will help someone.
I was getting the same error:
Error><Message>No HTTP resource was found that matches the request URI 'http://localhost:53569/api/values'.</Message><MessageDetail>No type was found that matches the controller named 'values'.</MessageDetail></Error>
By default when I created new controller in asp.net web forms application, it was like this:
ValuesController1 : ApiController
I just simply removed "1", and make it:
ValuesController : ApiController
And it works, don't know if it is a bug or whatever, but it made big trouble for me.

Why is the ASP.NET routing engine ignoring files?

I have a class that implements both IHttpHandler and IRouteHandler:
public class CustomHandler : IHttpHandler,IRouteHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.AddHeader("Content-Type", "text/plain");
context.Response.Write("Hello World");
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return this;
}
}
In the Application_Start method I try to register my handler with route:
Route route = new Route("dav/{*Pathinfo}", new CustomHandler());
RouteTable.Routes.Add(route);
Everything is cool until I call with this kind Url:
- http://localhost:63428/dav/asdadsa
- http://localhost:63428/dav/asdadsa/asdasd
- http://localhost:63428/dav/asdadsa/a%20%20sdasd (with space in the url)
But if I try with theese:
- http://localhost:63428/dav/asdadsa.docx
- http://localhost:63428/dav/asdads/a.docx
My handler not called and the server return with 404. I thought that the wildcard will match every url which start with dav/.
Any idea how to achive that the urls with extensions also match to my route?
UPDATE:
I found this page.
It's set from config, not from code behind but don't have to set the runAllManagedModulesForAllRequests settings and unfortunately don't get the route values so clean that in my original example.
Maybe somebody will be intrested if come to this question for answers.
If you add the following configuration to your web.config file then your routing will include files as well.
<configuration>
<system.webServer>
<modules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
<!-- more -->
</modules>
</system.webServer>
</configuration>
One different solution is to add <modules runAllManagedModulesForAllRequests="true"> but that gives an overhead in that all your registered HTTP modules run on every request, not just managed requests (e.g. .aspx). This means modules will run on every .jpg .gif .css .html .pdf etc.
You can read more here about the different routing settings.
Just remember, if you're adding your special routing to the existing routing you have to add your routing first or it won't be processed, as in this example.
Route route = new Route("dav/{*Pathinfo}", new CustomHandler());
routes.Add(route);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
One problem with this approach is that the first route definition confuses Html helpers in your system so you won't any longer get nice routes like localhost/home/index but instead localhost/dav?action=index&controller=home. The solution to this is to restrict the first route to only be valid on incoming route requests. This can be done by creating a RouteConstraint and add it to the route definition in a RouteValueDictionary.
public class OnlyIncomingRequestConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.IncomingRequest)
return true;
return false;
}
}
You can then redefine your route definition in this way:
Route route = new Route("dav/{*Pathinfo}", new RouteValueDictionary(new { controller = new OnlyIncomingRequestConstraint() }), new CustomHandler());
routes.Add(route);
After this your default routes should be back to normal again.

Resources