I have a MVC Web API get method that I'd like to be able to pass a nullable Guid as a parameter. If I setup the GET with a "?Id=null" I get a 400 response. I can pass a empty guid but that I'd rather not do that.
No matter what I change the URI to, "id=, id=null etc" it won't accept null. Does anyone know how to make this work?
[HttpGet]
public User Get(Guid? Id)
Update Route config
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Full Http Get signature, sourceId is the param that id like to pass as null.
[HttpGet]
public IEnumerable<ActionItemsListViewModel> GetPagedList(int skip, int take, int page, int pageSize, [FromUri]List<GridSortInfo> sort, [FromUri] ActionItem.ActionItemStatusTypes? actionItemStatus, Guid? sourceId)
Found the problem, this filter was saying the ModelState was invalid.
public class ApiValidationActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid )
{
var errors = actionContext.ModelState
.Where(e => e.Value.Errors.Count > 0)
.Select(e => e.Value.Errors.First().ErrorMessage).ToList();
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, string.Join(" ", errors));
}
}
}
Try to use:
[HttpGet]
public User Get(Guid? Id = null)
I was able to pass null to the Guid? when I use
query string parameter: api/values?id=null
route parameter: api/values/null
Controller:
public class ValuesController : ApiController
{
public User Get(Guid? Id)
{ ... }
}
Related
I have a Web API with about 6 stored procedures, each stored procedures has different parameter, for example 2 params or 3 params or 1 param and one stored procedures takes 0 param.
How can I pass those params to my web api?
This is my webConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
And my Controllers code:
public class channelsController : ApiController
{
[HttpGet]
[Route("api/channels/{code:string}/{search_by:string}")]
public IEnumerable<sp_get_channels_Result> Get(string code,string search)
{
myEntities db = new myEntities();
return db.sp_get_channels("", "all").AsEnumerable();
}
}
//no param
public class networksController : ApiController
{
[HttpGet]
public IEnumerable<sp_get_networks_Result> Get()
{
myEntities db = new myEntities();
return db.sp_get_networks().AsEnumerable();
}
}
But I get this error:
The inline constraint resolver of type DefaultInlineConstraintResolver was unable to resolve the following inline constraint: string.
can you try to change "search_by" into "search"
[HttpGet]
[Route("api/channels/{code}/{search}")]
public IEnumerable<sp_get_channels_Result> Get(string code,string search)
{
myEntities db = new myEntities();
return db.sp_get_channels("", "all").AsEnumerable();
}
:string
Is not a valid route constraint.
See the list of default constraints here: https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#route-constraints
You could write your own, but it's not there by default.
Execution of the following lines
#Html.ActionLink("Open",actionName: "DownloadExcelFile",
controllerName: "Excel",
routeValues: new { model = new ExcelModel(5, "JobName", "item.UserName") },
htmlAttributes: null)
returns Server Error in '/' Application, could you help me to fix them?
Note that when I change the name of the parameter, model -> id, I get an Error 404 instead of Server Error in '/' Application.
The model is
public class ExcelModel
{
public int InputA { get; set; }
public string InputB { get; set; }
public string UserName { get; set; }
public ExcelModel(int inputA, string inputB, string userName)
{
InputA = inputA;
InputB = inputB;
UserName = userName;
}
public ExcelModel()
{
InputA = 1;
InputB = "B1";
UserName = "NiceUser";
}
...
}
Controller is
public class ExcelController : Controller
{
public ActionResult Index()
{
var model = new ExcelModel(1, "B1", User.Identity.Name);
return View(model);
}
[HttpPost]
public ActionResult DownloadExcelFile(ExcelModel id)
{
// assume we create an an excel stream...
MemoryStream excelStream = id.BuildSpreadsheet();
return new FileStreamResult(excelStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = "myfile.xslx"
};
}
}
RouteConfig is the standard one
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 }
);
}
}
Finally, as mentioned earlier, the method itself is fine, since it works perfectly with submit, as below:
#using (Html.BeginForm("DownloadExcelFile", "Excel"))
{
<fieldset>
// fields names and values
<p>
<input type="submit" value="Open Excel"/>
</p>
</fieldset>
}
1) You can't pass an entire class as a route value param. The helper has to be able to put whatever you pass into a URL, which means it has to be something that can be converted to a string value. It might be possible to JSON encode the model and then pass the JSON string for the param, but the helper isn't going to make such assumptions for you, nor would it necessarily know how to JSON encode it for you, if it did.
2) When you just pass the id, you get a 404 because your action doesn't not accept an int for id, but rather expects ExcelModel, which as we discussed in #1, is not possible to pass via URL.
Your controller method is marked with the HttpPost attribute. This means that it only accepts POST-requests and not GET-requests. Normal link visits are GET-requests, so that is probably the problem. (Read more here)
Remove the HttpPost attribute and see if that fixes the problem.
I'm sending multiple requests to the same action method in a controller all these requests have some common querystring attributes and some specific to that request.
request1 : http://localhost/home/test?a=a1&b=b1&c=c1&d=d1....around 25 parameters
request2 : http://localhost/home/test?a=a1&b=b1&j=j1&k=k1...around 20 parameters
similarly request 3 , request4,etc...
My action method in mvc in homecontroller is as below..
public string test(string a, string b, string c, string d, ...around 50 parameters)
This is working perfectly..
But when I take this code and move it to web api, it no longer works..
Moreover, if I try with just two parameters, it works and I can get the two parameters..
public string test(string a, string b)
I have no control on the requests that I receive in my application as it is coming from a 3rd party host application, so the method name and parameters can not change ...
The route configured in mvc in route.config is standard..
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I have configured a separate route for webapi in webapiconfig on similar lines..
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Any ideas how to solve this..
Thanks
Arnab
The reason is that Web API does action overloading and all these parameters are required, if they are not provided you are ending up with 404. The simple answer to your question is to make them optional by giving them a default value, so your signature will look like this:
public IHttpActionResult Get(string a = null, string b = null, ...)
However this code seems very elaborate for what you are doing, it's probably also not the most efficient and you end up with a lot of if statements.
Consider alternatively just parsing the query string yourself and get a more convenient to use data set.
public class ValuesController : ApiController
{
public IHttpActionResult Get()
{
var collection = Request.RequestUri.ParseQueryString();
foreach (var key in collection.Keys)
{
var value = collection[(string)key];
// do something with key & value
}
return Ok();
}
}
and as another option is to build a model including all the parameters, something like:
public class Settings
{
public string A { get; set; }
public string B { get; set; }
...
}
and bind to the model using the FromUri:
public IHttpActionResult Get([FromUri]Settings settings)
{
...
}
Here is a link from Mike Stall's blog - http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx
I have a database with two tables. Countries and Cities where each city has a relation to a spesific country.
In my ASP.Net Web API I can get a list of countries by a GET request to http://example.com/api/countries to run the CountriesController. And I can get details about a country by http://example.com/api/countries/1.
If I want a list of all cities for a country the REST query URL should be http://example.com/api/countries/1/cities? And details for a City http://example.com/api/countries/1/cities/1
How can I accomplish this in ASP.Net Web API?
How about this, in global.asax.cs define an additional api route like so:
routes.MapHttpRoute(
name: "CityDetail",
routeTemplate: "api/countries/{countryid}/cities/{cityid}",
defaults: new { controller = "Cities" }
);
Then define a new CitiesController like so:
public class CitiesController : ApiController
{
// GET /api/values
public IEnumerable Get()
{
return new string[] { "value1", "value2" };
}
// GET /api/values/5
public string Get(int countryId, int cityid)
{
return "value";
}
// POST /api/values
public void Post(string value)
{
}
// PUT /api/values/5
public void Put(int countryId, int cityid, string value)
{
}
// DELETE /api/values/5
public void Delete(int countryId, int cityid)
{
}
}
Needless to say you might want to improve the controller implementation a bit :)
I have this set of routes:
routes.MapRoute(
"IssueType",
"issue/{type}",
new { controller = "Issue", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Here is the controller class:
public class IssueController : Controller
{
public ActionResult Index()
{
// todo: redirect to concrete type
return View();
}
public ActionResult Index(string type)
{
return View();
}
}
why, when i request http://host/issue i get The current request for action 'Index' on controller type 'IssueController' is ambiguous between the following action methods:
I expect that first one method should act when there is no parameters, and second one when some parameter specified.
where did i made mistake?
UPD: possible duplicate: Can you overload controller methods in ASP.NET MVC?
UPD 2: due to the link above - there is no any legal way to make action overloading, is it?
UPD 3: Action methods cannot be overloaded based on parameters (c) http://msdn.microsoft.com/en-us/library/system.web.mvc.controller%28VS.100%29.aspx
I would have one Index method that looks for a valid type variable
public class IssueController : Controller
{
public ActionResult Index(string type)
{
if(string.isNullOrEmpty(type)){
return View("viewWithOutType");}
else{
return View("viewWithType");}
}
}
EDIT:
How about creating a custom attribute that looks for a specific request value as in this post StackOverflow
[RequireRequestValue("someInt")]
public ActionResult MyMethod(int someInt) { /* ... */ }
[RequireRequestValue("someString")]
public ActionResult MyMethod(string someString) { /* ... */ }
public class RequireRequestValueAttribute : ActionMethodSelectorAttribute {
public RequireRequestValueAttribute(string valueName) {
ValueName = valueName;
}
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
return (controllerContext.HttpContext.Request[ValueName] != null);
}
public string ValueName { get; private set; }
}
I ran into a similar situation where I wanted my "Index" action to handle the rendering if I had an ID specified or not. The solution I came upon was to make the ID parameter to the Index method optional.
For example, I originally tried having both:
public ViewResult Index()
{
//...
}
// AND
public ViewResult Index(int entryId)
{
//...
}
and I just combined them and changed it to:
public ViewResult Index(int entryId = 0)
{
//...
}
You can do it using an ActionFilterAttribute that checks the parameters using reflection (I tried it) but it's a bad idea. Each distinct action should have its own name.
Why not just call your two methods "Index" and "Single", say, and live with the limitation on naming?
Unlike methods that are bound at compile time based on matching signatures, a missing route value at the end is treated like a null.
If you want the [hack] ActionFilterAttribute that matches parameters let me know and I'll post a link to it, but like I said, it's a bad idea.
All you have to do is mark your second Action with [HttpPost]. For instance:
public class IssueController : Controller
{
public ActionResult Index()
{
// todo: redirect to concrete type
return View();
}
[HttpPost]
public ActionResult Index(string type)
{
return View();
}
}