Need to pass a URL parameter to web API - asp.net-mvc

I have a MVC app with most controllers of type System.Web.MVC.Controller. There is one Web API controller also of type System.Web.Http.ApiController.
The Web API controller has an action that needs to accept URL as a parameter, something like this:
[HttpGet]
public HttpResponseMessage Image(string path)
{
//download and return image using WebClient library
}
On the view I would like to use the Image action something like
<image src="/api/image/?path=/remote/relative/path/someimage.jpg" alt="some description" />
I'm using following route template:
config.Routes.MapHttpRoute(
name: "api-image",
routeTemplate: "/api/image/{path}",
defaults: new { controller = "WebApiClient", action = "Image" }
);
The Image action only gets called for paths like /api/image/someimage-1.jpg or /api/image/someimage-2.jpg
Question:
The Image action does not get called for long paths like /api/image/?path=/remote/relative/path/someimage.jpg
If I encode the long path with HttpUtility.UrlEncode, it simply goes to global Not Found action (configured via web.config).
But if I don't encode it, "Dangerous characters found in path" exception gets thrown.
What am I doing wrong?

Try to add * to the route template:
config.Routes.MapHttpRoute(
name: "api-image",
routeTemplate: "/api/image/{*path}",
defaults: new { controller = "WebApiClient", action = "Image" }
);
and use it without encoding.

Related

Best way to give each customer a custom vanity URL

I allow each customer to choose their own URL, by baking their own custom code into the url as the first segment after the domain like this (https://www.[mydomain].com/customercode) and then learn who they are from that URL. This is working fine by learning who they are using MVC routing and session. The problem is that once the customer begins their session using their custom URL, the URL changes and they no longer see their name in the URL. i.e. they start the session as https://www.[mydomain].com/customercode, then the URL changes to something like https://www.[mydomain].com/account/login after the first re-direct.
Is there a way for me to preserve that customer info segment of the URL and just consider it as part of 'the base url'?
In the past I have written extensions such as CustomActionLink and CustomAction functions to override standard url rendering but that would mean that I need to use those functions all over my site. I am wondering if I manipulate or dictate every url rendered by my site, so that the customer name always appears in their url bar.
For reference, here is my routing code
// https://scheduler.[theservicedomain].com/TheCustomersName
routes.MapRoute(
name: "Scheduler",
url: "{practiceRouteCode}",
defaults: new { controller = "Scheduler", action = "Index" }
);
// https://scheduler.theservicedomain].com/scheduler/selectservice
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "Scheduler", action = "Index" },
namespaces: new[] { "Scheduler.Controllers" }
);
//https://scheduler.theservicedomain].com/TheCustomersName/scheduler/selectdate
routes.MapRoute(
name: "VanityURL",
url: "{practiceRouteCode}/{controller}/{action}",
defaults: new { controller = "Scheduler", action = "Index" },
namespaces: new[] { "Scheduler.Controllers" }
);
The customer is always required to begin the session with the first or third route, then we know who they are. But as of now the customer information (practiceRouteCode) is dropped and the rest of the session is on the second (Default) route.

"ExceptionMessage": "Multiple actions were found that match the request:

Hi found many helpful link to solve this but nothing is working to me :(. Can any one look at this as where I am going wrong?
Controller :
[ActionName("savebook")]
[HttpPost]
public HttpResponseMessage PostSaveBook([FromBody]Book product)
{
return Add(product);
}
[ActionName("savemobile")]
[HttpPost]
public HttpResponseMessage PostSaveMobile([FromBody]Mobile product)
{
return Add(product);
}
WebApiRouteConfig.
config.Routes.MapHttpRoute(
name: "ControllerOnly",
routeTemplate: "api/{controller}"
);
// Controller with ID
// To handle routes like `/api/VTRouting/1`
config.Routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" } // Only integers
);
// Controllers with Actions
// To handle routes like `/api/VTRouting/route`
config.Routes.MapHttpRoute(
name: "ControllerAndAction",
routeTemplate: "api/{controller}/{action}"
);
Error :
**
"ExceptionMessage": "Multiple actions were found that match the
request:
**
The first route that you have registered is the RESTful style route, which doesn't include an {action} segment.
As the routes are evaluated in the same order that they are registered, whenever you send a POST request, the controller name will be taken from the URL, and then, the action selector will look for an action whose name starts with Post. In your case there are two actions that fulfill this condition: PostSaveBook and PostSaveMobile.
If the first registered route was the "ControllerAndAction" one, the action would be taken from the URL and the action selector could correctly choose the desired action.
If you want to use the RESTful style, you'd have to use attribute routing to break the ambiguity.

MVC routes.MapRoute name property

I'm brand new to MVC so please bear with me as I'm only on the second page of the MS Tutorial (see last code example). For the HelloWorldController the following MapRoute is added:
routes.MapRoute(
name: "Hello",
url: "{controller}/{action}/{name}/{id}");
I'm just wondering, is it purely the pattern matching that does the work and the name "Hello" is just for my own reference? If so, are there not naming conventions that should be followed saying the MapRoute should be called HelloWorldWelcome, where welcome is a method inside the HelloWorldController.cs (see above link). Or am i being pedantic?
The route name is also used by the UrlHelper class. For example:
var url = Url.Route("Hello", new
{
controller = "SomeController",
action = "SomeAction",
name = "charlie",
id = 123
});
This will generate a matching URL.
This feature is much more useful when you use Attribute Routing. For example, if on some controller you have an action:
[RoutePrefix("api/phonebook")]
public class PhonebookController
{
[HttpGet("contact/{id}", Name = "GetContact")]
public Contact GetContact(int id)
{
...
}
}
In other code you could use Url.Route("GetContact", new { id = 7 }) to generate the URL /api/phonebook/contact/7.
Please refer to details on ASP.NET MVC Routing Overview
Name attribute is for callign a route from your views or controller with route name.
From ActionLink your can use a routename:
Html.RouteLink("link_text", "route_name", route_parameters)
The question seems to be not so clearly answered (how the "Hello" route is choosen by the "HelloWorld" controller?), but as an Asp.Net MV5 begginer, I can see that the route is selected by default according to the match between the router url property and the URL parameters.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "ImageScan", action = "ScanImage", id = UrlParameter.Optional },
namespaces: new[] { "WebApplication3.Controllers" }
);
I am finding error :
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Views/ImageScan/ScanImage.cshtml

WebApi Routing - map url to api

I've been googling for about an hour and not found an example so I thought I'd ask here.
I am replacing a standard mvc controller with a webapi and having problems with the routing, I've changed the names because of the organisation but they are still valid.
my webapi is currently called SystemAPI in the controllers folder - I want it to have a different name that the url that points to it ideally.
The url I need to point to it is /v1Controller/{id}
I cant change the v1Controller url as it is a fixed point with apps I have no control over
my current coding attempt is
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DocumobiApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "V1Controller",
routeTemplate: "v1Controller/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
}
to which I get the response
"message":"No HTTP resource was found that matches the request URI 'http://localhost:38685/v1Controller'.","messageDetail":"No route providing a controller name was found to match request URI 'http://localhost:38685/v1Controller'"}
I'm sure its something fairly obvious but cant for the life of me figure it out.
Cheers,
Steven
As the following error message says, a route match should result in providing value for the controller route variable which Web API depends on for selecting a controller.
No route providing a controller name was found to match request URI 'http://localhost:38685/v1Controller
So you could modify the route like below(notice the default value for controller variable):
config.Routes.MapHttpRoute(
name: "V1Controller",
routeTemplate: "v1Controller/{id}",
defaults: new { id = RouteParameter.Optional, controller="SomeControllerHere" }
);
Make sure your action method on your controller has an id with a default value.

Non Api routing with SPA and Web Api

I have been trying for a few days to solve this problem so I may in fact be trying to solve the wrong problem or don't know the right terms to search so here goes.
I'm creating a SPA with AngularJS and Web Api (4.5). All of my views are client side. So the server doesn't know all the possible routes that may become present in the URI. This isn't a problem until a user uses one of the browser controls (back, forward, refresh, history) and the server attempts to route the requested URI.
My thoughts on dealing with this have centered around mapping all non API routes to the root of the application.
So I would like to either, know the correct syntax for MapHttpRoute to pick up any route that does not attempt to reference controllers (API's) or if there is a better method for dealing with this problem.
public static void Register( HttpConfiguration config )
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//
// This doesn't work
//
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{*catchall}"
);
}
This XML file does not appear to have any style information associated with it. The document tree is shown below.
No HTTP resource was found that matches the request URI 'http://localhost:9234/login'.
No route providing a controller name was found to match request URI 'http://localhost:9234/login'
Not a good idea, just workaround
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "{*catchall}",
defaults: new { controller = "Spa"}
);
[AllowAnonymous]
public sealed class SpaController : ApiController
{
public HttpResponseMessage Get()
{
var indexHtml = HostingEnvironment.MapPath("~/index.html");
var stringContent = new StreamContent(File.OpenRead(indexHtml));
var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = stringContent};
return response;
}
}

Resources