ASP.Net MVC: Routing issue which throwing exception - asp.net-mvc-5

I have a test controller where i have one index action which accept custid as a argument.
this is how my controller looks
public class TestController : Controller
{
// GET: Test
public ActionResult Index(int custid)
{
return View();
}
}
i have added one extra routing statement in route.config file.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "custom1",
url: "{controller}/{id}",
defaults: new { controller = "Test", action = "Index", custid = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
so when accessing test controller action with url like localhost:50675/test/101 then getting error. error message say
The parameters dictionary contains a null entry for parameter 'custid'
of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Index(Int32)' in
'WebAPICRUD.Controllers.TestController'. An optional parameter must be
a reference type, a nullable type, or be declared as an optional
parameter. Parameter name: parameters
but when accessing test controller action with url like localhost:50675/test?custid=101 then getting no error.
so i do not understand what mistake is there in code.
What i need to do as a result i can issue this url http://localhost:50675/test/101 which should work. please guide me. thanks

Your route definition need to contain a segment for custid (not id) to match the name of the parameter. The route definition should also contain the name of the controller to make it unique
routes.MapRoute(
name: "custom1",
url: "Test/{custid}", // modify
defaults: new { controller = "Test", action = "Index"}
);
Note that you can also remove the custid = UrlParameter.Optional since you do not want it to be optional

Related

How to override a route in MVC 5

I've two controller controllerOne is defined under an area AreaOne . I need to reroute one of the action defined in the controllerOne to the action defined in controllerTwo I tried attribute routing but its not working. my current code is given below
Action In Controller One
public class ControllerOne
{ public ActionResult CustomerSearch()
{
return View("Search", model);
}
}
Action In Controller Two
[Route("AreaOne/ControllerOne/CustomerSearch")]
public class ControllerTwo
{ public ActionResult CustomCustomerSearch()
{
return View("Search", model);
}
}
How can I achieve this.?
In RouteConfig.cs you can use routes.MapRoute to associate your URL with any controller & action in your project.
routes.MapRoute(
name: "OverrideCustomerSearch",
url: "AreaOne/ControllerOne/CustomerSearch",
defaults: new { controller = "Two", action = "CustomCustomerSearch" }
);

Customize Hyprlinkr to use the name of a route's template

Context
Given the following route
config.Routes.MapHttpRoute(
name: "FooBarBazRoute",
routeTemplate: "foo-bar-baz/{id}",
defaults: new
{
controller = "FooBarBaz",
id = RouteParameter.Optional
});
and by using Hyprlinkr
var linker = new RouteLinker(this.Request);
var id = "813bafcc-8329-<trimmed>"
var href = this
.linker
.GetUri<FooBarBazController>(
c => c.Get(id))
.ToString()
the value of href looks like this:
"http://d59503db-1e96-<trimmed>/foobarbaz/813bafcc-8329-<trimmed>"
Question
Is it possible to customize Hyprlinkr so that href looks like:
"http://d59503db-1e96-<trimmed>/foo-bar-baz/813bafcc-8329-<trimmed>"
That is, instead of foobarbaz, it'd be nice if Hyprlinkr creates foo-bar-baz – similar to the route template, which is foo-bar-baz/{id}.
Yes, this is possible. That's one of the main reasons for the existence of the IRouteDispatcher interface in Hyprlinkr.
Define a custom implementation of IRouteDispatcher, like this:
public class MyDispatcher : IRouteDispatcher
{
private readonly IRouteDispatcher dispatcher;
public MyDispatcher(IRouteDispatcher dispatcher)
{
this.dispatcher = dispatcher;
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues)
{
if (method.Method.ReflectedType == typeof(FooBarBazController))
return new Rouple("FooBarBazRoute", routeValues);
return this.dispatcher.Dispatch(method, routeValues);
}
}
Use this to create your RouteLinker instance:
var linker = new RouteLinker(
request,
new MyDispatcher(new DefaultRouteDispatcher()));
You can add more special cases to MyDispatcher.Dispatch if you have more named routes to which you need to dispatch.

ServiceStack and dynamic properties in request DTOs

I would like to post a JSON object to my service stack service and use a dynamic property in the request DTO. All approaches I have tried so far leave the object being a NULL value.
The javascript code I use:
$.getJSON(
"/api/json/reply/Hello",
{
Name: "Murphy",
Laws: {
SomeProp: "A list of my laws",
SomeArr: [
{ Title: "First law" },
{ Title: "Second law" },
{ Title: "Third law" }
]
}
},
function(data) {
alert(data.result);
}
);
The DTO to receive the request:
public class Hello
{
public string Name { get; set; }
public dynamic Laws { get; set; }
}
I also tried to use an object and JsonObject instead of dynamic in the DTO.
To be complete, here's the service too:
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Murphy comes through in the Name property without any problems, but the Laws property remains NULL.
In the end, I want to somehow iterate (using reflection?) over the Laws property and get all the contained properties and values.
I cannot use a typed DTO here, because I don't know the JSON of the Laws property at development time (and it can change quite frequently).
Thanks for any help!
The .NET 3.5 library builds of ServiceStack on NuGet doesn't have native support for the .NET 4.0+ dynamic type. You can pass JSON into a string property and dynamically parse it on the server:
public object Any(Hello request)
{
var laws = JsonObject.Parse(request.Laws);
laws["SomeProp"] //
laws.ArrayObjects("SomeArr") //
}
Otherwise You can use Dictionary<string,string> or if you specify in your AppHost:
JsConfig.ConvertObjectTypesIntoStringDictionary = true;
You can use object which will treat objects like a string dictionary.
Otherwise dynamic shouldn't be on the DTO as it's meaningless as to what the service expects. You could just add it to the QueryString. You can use the JSV Format to specify complex object graphs in the QueryString, e.g:
/hello?laws={SomeProp:A list of my laws,SomeArr:[{Title:First Law}]}
Note: the spaces above gets encoded with %20 on the wire.
Which you can access in your services with:
public object Any(Hello request)
{
var laws = base.QueryString["laws"].FromJsv<SomeTypeMatchingJsvSent>();
}

How to use receive POST data in asp.net web api?

I've googled a whole day but still can't find the answer. I need to POST data via jQuery.post to Web API MVC-4 but unable to. This is my routing:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
and this is my Controller (the GET works!):
public string Get(int id)
{
return "value";
}
public void Post([FromBody]string data)
{
//body...
}
This is the jQuery.post:
$.post('api/mycontroller', { key1: 'val1' });
Any idea ?
Edit:
#Darin: I tried this:
public class UnitDetails{
public string id { get; set; }
}
and:
public void Post(UnitDetails id) {
//body...
}
and:
$.post('api/mycontroller', {id:'string1'});
But still I miss something.. it doesn't stop in Post(...){...}. Again - Get(...){...} does work.. ?
This is by design and the only way to make this work with a primitive type such as a string is the following:
$.post('/api/mycontroller', '=' + encodeURIComponent('val1'));
So the body of the POST request must contain the following:
=val1
instead of:
data=val1
This has been discussed in this thread.
As an alternative you could define a view model:
public class MyViewModel
{
public string Data { get; set; }
}
and then have your controller action take this view model as parameter:
public void Post(MyViewModel model)
{
//body...
}
Contrary to primitive types, complex types use formatters instead of model binding. Here's an article which covers how does the Web API does parameter binding.
You're posting to api/mycontroller. ASP.NET MVC automatically appends the name supplied with 'Controller', so it's looking for a controller named mycontrollerController. The name of your API controller is not mentioned in your post, but I suspect it's not that.
Assuming that your controller is named 'myController', try posting to api/my.
$.post('api/my', { id: 'string1' });

How to handle hierarchical routes in ASP.NET Web API?

Currently I have two controllers
1 - Parent Controller
2 - Child Controller
I access my Parent Controller like this
someurl\parentcontroller
Now I want to access my children controller like this
someurl\parentcontroller\1\childcontroller
This last url should return all the children of a particular parent.
I have this route currently in my global.asax file
routes.MapHttpRoute ("Route1", "{controller}/{id}", new { id = RouteParameter.Optional });
I am not sure how can I achieve my parent\id\child hierarchy.. How should I configure my routes to achieve this? Ideas?
Configure the routes as below. The {param} is optional (use if you need):
routes.MapHttpRoute(
name: "childapi",
routeTemplate: "api/Parent/{id}/Child/{param}",
defaults: new { controller = "Child", param = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Then call the child APi as /api/Parent/1/child
The parent can be called simple as /api/Parent/
The child controller:
public class ChildController : ApiController
{
public string Get(int id)
{
//the id is id between parent/{id}/child
return "value";
}
.......
}
Since Web API 2 you can now use Route Attributes to define custom routing per Method,
[Route("api/customers/{id:guid}/orders")]
public IEnumerable<Order> GetCustomerOrders(Guid id) {
return new Order[0];
}
You also need to add following line to WebApiConfig.Register() initialization method,
config.MapHttpAttributeRoutes();
Full article,
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
I wanted to handle this in a more general way, instead of wiring up a ChildController directly with controller = "Child", as Abhijit Kadam did. I have several child controllers and didn't want to have to map a specific route for each one, with controller = "ChildX" and controller = "ChildY" over and over.
My WebApiConfig looks like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ChildApi",
routeTemplate: "api/{parentController}/{parentId}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
My parent controllers are very standard, and match the default route above. A sample child controller looks like this:
public class CommentController : ApiController
{
// GET api/product/5/comment
public string Get(ParentController parentController, string parentId)
{
return "This is the comment controller with parent of "
+ parentId + ", which is a " + parentController.ToString();
}
// GET api/product/5/comment/122
public string Get(ParentController parentController, string parentId,
string id)
{
return "You are looking for comment " + id + " under parent "
+ parentId + ", which is a "
+ parentController.ToString();
}
}
public enum ParentController
{
Product
}
Some drawbacks of my implementation
As you can see, I used an enum, so I'm still having to manage parent controllers in two separate places. It could have just as easily been a string parameter, but I wanted to prevent api/crazy-non-existent-parent/5/comment/122 from working.
There's probably a way to use reflection or something to do this on the fly without managing it separetly, but this works for me for now.
It doesn't support children of children.
There's probably a better solution that's even more general, but like I said, this works for me.
An option beyond using default mvc routing is to look at Attribute Routing - https://github.com/mccalltd/AttributeRouting. Although its more work, decorating individual action methods provides a ton of flexibility when you need to design complicated routes. You can also use it in conjunction with standard MVC routing.

Resources