ASP.NET core HttpGet single Web API - asp.net-web-api

Good Morning,
I’m having difficulty setting up my HTTPGETs and then testing the solution in Postman.
I’m trying to return a single result on both occasions however when I input the parameters nothing loads. So I'm clearly missing something which i need some help on please.
I have 1 parameter {id} in my CashMovementController and if I navigate to localhost/api/cashmovements/{id} it loads however if pass the {id} parameter in postman it fails.
Then in my BondCreditRatingsController I have 2 parameters {ISIN} & {Date} and again I'm not sure how to approach this.
Love to hear some advice/help on this please
Thanks GWS
Startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
CashMovementsController.cs
[Route("api/[controller]")]
public class CashMovementsController : Controller
{
private ICashMovementRepository _cashmovementRepository;
[HttpGet("{id}", Name = "GetCashMovement")]
public IActionResult Get(int id)
{
CashMovement _cashmovement = _cashmovementRepository.GetSingle(u => u.CashMovementId == id);
if (_cashmovement != null)
{
CashMovementViewModel _cashmovementVM = Mapper.Map<CashMovement, CashMovementViewModel>(_cashmovement);
return new OkObjectResult(_cashmovementVM);
}
else
{
return NotFound();
}
}
}
BondCreditRatingsController.cs
[Route("api/[controller]")]
public class BondCreditRatingsController : Controller
{
private IBondCreditRatingRepository _bondcreditratingRepository;
public BondCreditRatingsController(IBondCreditRatingRepository bondcreditratingRepository)
{
_bondcreditratingRepository = bondcreditratingRepository;
}
[HttpGet("{id}", Name = "GetBondCreditRating")]
public IActionResult Get(string id, DateTime efffectivedate)
{
BondCreditRating _bondcreditrating = _bondcreditratingRepository.GetSingle(u => u.ISIN == id, u => u.EffectiveDate == efffectivedate);
if (_bondcreditrating != null)
{
BondCreditRatingViewModel _bondcreditratingVM = Mapper.Map<BondCreditRating, BondCreditRatingViewModel>(_bondcreditrating);
return new OkObjectResult(_bondcreditratingVM);
}
else
{
return NotFound();
}
}

If you want to map it to api/Controller/method/id you would need to use the code below because you want to map parameter order (no other identifier) to a specific parameter name in the action.
[HttpGet("GetCashMovement/{id}")]
Your current code should work with below since you are using named parameters and because the request can't be mapped to any other template.
/api/CashMovements/GetCashMovement?id=1
But that attribute syntax will also (possibly unintentionally) trigger:
/api/CashMovements/1
Since a sum of your defined template for that action is:
[Route("api/[controller]/{id}")]
Reason to why /api/ApiTest/GetCashMovement maps GetCashMovement.Get(int i) is because id is defined as optional in startup
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/**{id?}**");
A question mark (?) after the route parameter name defines an optional
parameter.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-3.0#create-routes

Related

Context.Request.Query["id"] return null in View

I want to get "id" parameter in View, but Context.Request.Query["id"] return null value.
Query like this:localhost:1000/MyController/Getuser/65190907-1145-7049-9baa-d68d44b1ad06
// Controller
public ActionResult Getuser(Guid id)
{
//HttpContext.Request.Query["id"] also return null
return View();
}
//in startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
I think I can get it by ViewContext.ModelState["id"].AttemptedValue
Request.Query contains query string of the request, i.e. URL part that goes after question mark: ...?param1=value1&param2=value2. URL localhost:1000/MyController/Getuser/65190907-1145-7049-9baa-d68d44b1ad06 does not contain query string. GUID 65190907-1145-7049-9baa-d68d44b1ad06 is just a part of URL path.
If for some reason you want to access id parameter from raw request, not via Model Binding, you have two options:
Pass id in query string and access it via HttpContext.Request.Query["id"]:
In this case request URL will be http://localhost:1000/MyController/Getuser?id=65190907-1145-7049-9baa-d68d44b1ad06. No changes in routes are required.
The second option is to extract id from Request.Path:
public IActionResult Getuser(Guid id)
{
var path = HttpContext.Request.Path;
var id2 = Guid.Parse(path.Value.Split('/').Last());
return View();
}

MVC Web API Routing to Wrong Action

I have a web API controller, when I call the default Get action this works, when I call another specific action (GetReservationsForCustomer) this also works, but one action gives an error (GetReservationsByDate), it seems to route to the default Get action. Here is the code:
// GET: api/Reservations
public IQueryable<Reservation> GetReservations()
{
return db.Reservations;
}
[ResponseType(typeof(ReservationDTO))]
public IHttpActionResult GetReservationsForCustomer(int CustomerId)
{
IEnumerable<Reservation> reservations = db.Reservations.Where(r => r.CustomerId == CustomerId).ToList();
List<ReservationDTO> reservationList = new List<ReservationDTO>();
foreach(Reservation reservation in reservations)
{
reservationList.Add(new ReservationDTO
{
id = reservation.id,
ReservationStart = reservation.ReservationStart,
Covers = reservation.Covers
});
}
return Ok(reservationList);
}
[ResponseType(typeof(ListReservationDTO))]
public IHttpActionResult GetReservationsByDate(DateTime StartDate, DateTime EndDate)
{
IEnumerable<Reservation> reservations = new List<Reservation>();
if (EndDate != null)
{
reservations = db.Reservations.Where(r => r.ReservationStart.Date >= StartDate.Date && r.ReservationStart.Date >= EndDate.Date).ToList();
}
else
{
reservations = db.Reservations.Where(r => r.ReservationStart.Date == StartDate.Date).ToList();
}
List<ReservationDTO> reservationList = new List<ReservationDTO>();
foreach (Reservation res in reservations)
{
reservationList.Add(new ReservationDTO
{
id = res.id,
ReservationStart = res.ReservationStart,
Covers = res.Covers,
CustomerEmail = res.Customer.EmailAddress,
CustomerName = res.Customer.Name,
CustomerPhone = res.Customer.PhoneNumber
});
}
return Ok(reservationList);
}
Here is my API call:
http://localhost:55601/api/Reservations/GetReservationsByDate/?StartDate=2018-03-04:T12:30:00
And here is the response:
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Http.IHttpActionResult GetReservation(Int32)' in 'GreenLionBookings.API.ReservationsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
Please note the specifics of the action are not relevant at this stage, I've butchered it a fair bit trying to get this to work! I've tried specifying a start date and end date and neither seems to work. It always seems to get routed to the default Get action.
Here is my RouteConfig:
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 }
);
}
}
And here is my WebApiConfig:
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();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
So both basically just default.
Why is this not routing to the correct action, just for this one and not the others? I have another controller (Customers) which seems to work correctly for all actions too. I've read this and this, and also this and this which I actually thought was pretty relevant and quite helpful, but didn't solve my problem.
What am I doing wrong here?
First of all, you have a typeo in the date.
This 2018-03-04:T12:30:00 should be 2018-03-04T12:30:00.
Then, to solve the routing problem, you could leave out the action name of the url and let the framework match the request against the parameters name.
Try it like this
api/Reservations?StartDate=2018-03-04T12:30:00&EndDate=2018-03-05T12:30:00
Then, if you want to be able to send nullable values to EndDate which is a value type of DateTime; make the DateTime nullable
[ResponseType(typeof(ListReservationDTO))]
public IHttpActionResult GetReservationsByDate(DateTime StartDate, DateTime? EndDate)
Notice the DateTime? which is a shorthand for Nullable<DateTime>

Url routing in mvc 3.0

I want to have below specific Url routing for my website
http://www.MyWebsite?Region=US&Area=South
based on passed Region and Area query string parameter, i want to redirect to a specific controller action.
Problems:
What should be the Url Routing ?
Whether creating a common controller action which redirects to specific action will work here?
Any help would be greatly appriciated !!
Thanks
If you want to do querystring right off the root, then you need to map routes in global this way. Inside your action you can access context and grab querystring from it. Below is my example to do twitter style usernames, but idea is the same.
routes.MapRoute("UserSettings",
"{username}/settings",
new { controller = "Home", action = "Settings", username = "" },
new { username = new NotNullOrEmptyConstraint() }
);
public class NotNullOrEmptyConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
try
{
return !String.IsNullOrEmpty(values["username"].ToString());
}
catch (Exception)
{
return false;
}
}
}
public ActionResult Settings(string username){
}

How do I validate for a empty query string parameter in asp.net mvc3

I want to validate for a empty Id value in the url.
../Task/EditEmployee/afccb22a-7cfd-4be5-8f82-9bd353c13b16
I want that if the Id is empty
../Task/EditEmployee/
Than redirect the user to a certain page.
public ActionResult EditEmployee(Guid Id)
{
//Some code in here
}
It may not be the best solution but you can take id parameter as string and try to parse it like this:
public ActionResult EditEmployee(string id)
{
if(string.IsNullOrWhiteSpace(id))
{
// handle empty querystring
}
else
{
Guid guid;
if (Guid.TryParse(id, out guid))
{
//Some code in here
}
}
}
Or
You can also create a regex constraint on the route but that may be too complicated and hard to understand. Map this route before the default one.
routes.MapRoute(
"TastEditEmployee",
"Task/EditEmployee/{id}",
new { controller = "Task", action = "EditEmployee" },
new { id = #"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$" }
);
Then you can use id parameter as Nullable Guid.
public ActionResult EditEmployee(Guid? id)
{
//do something
}
Since Guid is a struct, the value of Id will be Guid.Empty if it was omitted. You can check for that.
public ActionResult EditEmployee(Guid Id)
{
if (Id == Guid.Empty) throw new ArgumentException("Id not specified.");
}

Migrate custom route to ASP.NET MVC custom route to support List<string>

I have a URL in a Django based web app that looks similar to this:
/market/prices/2011-05-01/min/stocks/msft/dell/appl/
The application is being rewritten in ASP.NET MVC 3. I need to maintain the URL.
The crux of the problem is that I to support the multiple stock ticker symbols separated by forward slashes.
I want a custom route that looks like this:
routes.MapRoute(
"Stocks",
"{queryDate}/{minOrMax}/stocks/{listOfStocksSeparatedByForwardSlash}",
new { controller = "Market", action = "Prices" }
);
The controller would look something like:
public ActionResult Prices(string queryDate, string minOrMax, ICollection<string> listOfStocksSeparatedByForwardSlash) {
var model = repository.List(queryDate, minOrMax, listOfStocksSeparatedByForwardSlash);
return View(model );
}
My current solution is as follows:
routes.MapRoute(
"Stocks",
"{queryDate}/{minOrMax}/stocks/{*listOfStocksSeparatedByForwardSlash}",
new { controller = "Market", action = "Prices" }
);
public ActionResult Prices(string queryDate, string minOrMax, string listOfStocksSeparatedByForwardSlash) {
var list = listOfStocksSeparatedByForwardSlash.Split('/').ToList();
var model = repository.List(queryDate, minOrMax, list);
return View(model );
}
Although this works, I'm interested to know if there is a better way to do this?
Okay, this is an option, although I think your approach is easier.
You can provide a RouteHandler attached to a route, like so:
routes.MapRoute(
name: "Test",
url: "Test/{someDate}/{*tickerSymbols}",
defaults: new { controller = "Home", action = "Test" }).RouteHandler = new SlashSeparatedTrailingParametersRouteHandler("tickerSymbols", "tickers");
with the route handler being
public class SlashSeparatedTrailingParametersRouteHandler : IRouteHandler
{
private readonly string catchallParameterName;
private readonly string actionTargetParameter;
public SlashSeparatedTrailingParametersRouteHandler(string catchallParameterName, string actionTargetParameter)
{
this.catchallParameterName = catchallParameterName;
this.actionTargetParameter = actionTargetParameter;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
IRouteHandler handler = new MvcRouteHandler();
var vals = requestContext.RouteData.Values;
vals[this.actionTargetParameter] = vals[this.catchallParameterName].ToString().Split('/');
return handler.GetHttpHandler(requestContext);
}
}
If this is your controller action:
[HttpGet]
public ActionResult Test(DateTime someDate, string[] tickers)
{
if (tickers == null)
{
throw new ArgumentNullException("tickers");
}
return Content(string.Format("{0} and {1}", someDate, tickers.Length.ToString(CultureInfo.InvariantCulture)));
}
and this your post:
http://localhost/Test/12-06-2012/Foo/Bar
then your output is:
12/6/2012 12:00:00 AM and 2
On the elegance this this improves the parameter on the action method at the expense of having to write your own route handler.

Resources