ASP.NET Web API 2, how to distinguish methods with similar url - c#

My team are using ASP.NET Web API framework.
In our application, we have 2 method which look like this:
[Route("users/events"]
[HttpGet]
public UserEvent GetEventsAssociatedWithUser(string Id) { ... }
and
[Route("users/{Id}"]
[HttpGet]
public User GetUserInformation(string Id) { ... }
but whenever I want to send request to "...users/events", it keeps send it to "...users/{Id}" and use "events" as an URI parameter.
I just want to know if there is any way to solve this problems without changing the URL of any of these method?

You need to set a route order like this
[Route("users/events", RouteOrder = 1)]
Read more here: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#order

You need to use the RouteOrder parameter
See here:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#order
Example:
[Route("users/events" RouteOrder = 1]
[HttpGet]
public UserEvent GetEventsAssociatedWithUser(string Id) { ... }

Related

asp.net - how to get value from post method

In asp.net app i receive a form values from angular app.
[HttpPost]
public IActionResult addChange([FromBody] Change change)
{
return Json(change.Status);
}
How to get object or some value of object to use it in another class?
You should be able to access property like that: change.PropertyName.
But you might send data from angular as FormData, then you should change [FromBody] to [FromForm].
It's most likely that you are doing something wrong at angular site. You should check this endpoint via postman.
Edit:
To use this object in another method you should pass it through.
[HttpPost]
public IActionResult addChange([FromBody] Change change)
{
AnotherMethod(change);
return Json(change.Status);
}
public void AnotherMethod(Change change)
{
var foo = change.Status;
}

NSwag MSBuild "The method 'get' on path '/api/Account' is registered multiple times"

I'm trying to generate a swagger specification with NSwag.MSbuild but whenever I do it throws me this message:
The method 'get' on path '/api/Account' is registered multiple times
Now the problem is that my methods are route-less as shown below with some examples of the controller
[HttpPost]
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalRegister(string provider, string returnUrl = null)
[HttpGet]
public IActionResult AddLogin(string provider, string returnUrl)
[HttpGet]
[AllowAnonymous]
public ActionResult SignUpConfig()
I understand why it does this but what I don't understand is that doing the same thing in NSwag Studio works, the command I use is $(NSwagExe_Core22) webapi2swagger is there an option so that it generates successfully like NSwag Studio?
In a WebAPI if you have more than one HttpGet or HttpPost etc you should add Route Attribute to distinguish them.
Add HttpGet["{name}"]
Turns out you don't have to specify the routes if you don't want to it has something to do with the Default Url Template:
/DefaultUrlTemplate:"{controller}/{action}/{id?}"
adding {action} solved it for me
In my case, I had already added custom [Route("")] attributes to all the paths. The problem was I had two public helper methods in the controller which NSwag identified as GET methods. Simply making them private (which they should have been anyway) made the problem go away...
In my case I had
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(long byId, string color)
{
/* Do things one way */
}
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(string byName, string color)
{
/* Do things another way */
}
The problem was that there were two identically named routes that take in different parameters. This is an overload situation that ASP.NET seems to be perfectly fine with but apparently blows NSwag's mind.
Because this was in legacy code, renaming the methods was not an option for me so I created a single method with optional parameters like so:
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(string color, long? byId = null, string byName = null )
{
if (byId != null)
{
/* Do things one way */
}
else
{
/* Do things another way */
}
}
What helped me in this situation was to set the Route Attribute like this:
[Route("SignUpConfig")] ,[Route("AdLogin")]
If your controller is decorated with
[Route("[controller]")]
then you need you specify separate names
HttpGet("get1") and HttpGet("get2")
Else it will pick if decoration contains action name it it like
Route("[controller]/[action]") or from default route {controller}/{action}/{id?}

WebApi 2 routing with ? or /

I just added swagger to my api to generate some documentation...
normally, my front end code would do a "get by id" like this:
https://whatever.com/api/GetDisplayContainer/A90555CD-931E-4D9D-D51D-08D63E83FCC6
however, swaggers "try it" wants to send:
https://whatever.com/api/GetDisplayContainer?id=A90555CD-931E-4D9D-D51D-08D63E83FCC6
I want to be able to support both ways. How do I do it?
Here is an example of a controller method:
[HttpGet]
[Route("GetDisplayContainer")]
public ApiResponse<ContainerDisplayViewModel> GetDisplayContainer(Guid id)
{
return ApiResponse.Convert(ResourceService, _containerService.GetDisplayContainerById(id));
}
I don't really want to have to change my existing code to do it the "query string" way. because its a totally valid way of doing it. But it would be nice to be able to support both...
This is C# using .net core 2.1.
Thanks!
You can do two routes:
[HttpGet]
[Route("GetDisplayContainer")]
public ApiResponse<ContainerDisplayViewModel> GetDisplayContainer([FromQuery] Guid id)
{
}
and
[HttpGet]
[Route("GetDisplayContainer/{id}")]
public ApiResponse<ContainerDisplayViewModel> GetDisplayContainerRoute([FromRoute] Guid id)
{
}
If you change your route from GetDisplayContainer to GetDisplayContainer/{id} then Swagger will know that the parameter is not located in the query string and should generate your desired output.
Full code:
[HttpGet]
[Route("GetDisplayContainer/{id}")]
public ApiResponse<ContainerDisplayViewModel> GetDisplayContainer(Guid id)
{
return ApiResponse.Convert(ResourceService, _containerService.GetDisplayContainerById(id));
}

How to RedirectToAction with a model passed?

I have an model class that is used to validate some user input.
I have an controller with the following.
public IActionResult Checkout(GiftCard giftCard)
{
}
I was wondering how I could on an different action redirect it back to it such as
public IActionResult Preview(GiftCard giftCard)
{
return RedirectToAction("Checkout");
}
The above doesn't work because asp.net is trying to find an action without the model like the one below
public IActionResult Checkout()
{
}
if your url going to be really long make a shorturl so load that and redirect from there, if you use ajax it wont be visible
You could use action with another name and apply action selector to your renamed method. Like next:
[ActionName("Checkout")]
[HttpPost] //Recomend you send user input via post
[ValidateAntiForgeryToken] // and use validation token
public IActionResult CheckoutConfirmed(GiftCard giftCard)
{
//your code
}
public IActionResult Checkout()
{
//your code
}
Check out more about ASP.NET MVC - Selectors
If you need more information about ValidateAntiForgeryToken, you could find it there - Chapter 12: Security
And also, you could find great article about posting there - ASP.NET MVC Preview 5 and Form Posting Scenarios
RedirectToAction has a second parameter called routeValues with which you can pass the GiftCard like following.
public IActionResult Preview(GiftCard giftCard)
{
return RedirectToAction("Checkout", giftCard);
}

Getting angular ajax data in asp.net mvc6

I've been trying to figure this out for hours now but none of the solutions seem to help. I have an MVC6 project with AngularJs. I am able to connect, so my routes are working, and I am able to get data back if I hard code a string or something, but I can't seem to access the data sent to the server.
My angularjs http request code:
var app = angular.module('APIService', []);
app.factory('APIService', function ($http) {
var api = {};
api.getBuyer = function (data) {
return $http.post('/api/buyer', data);
}
return api;
});
The angularjs function call
APIService.getBuyer({ Url: 'samirbashir5739', FirstName: 'Samir' }).success(function (res) {
});
My C# Controller
namespace Reporting.api
{
[Route("api/buyer")]
public class BuyersController : Controller
{
// POST api/buyer
[HttpPost]
public string Post([FromBody] string Url)
{
return Url;
}
}
}
I've tried setting the data as "JsonResult data", or even "string Url." Most tutorials I found had an object for the data so it would fit into something like "[FromBody] Buyer buyer" but I don't have an object for it, I simply want the data. Is it possible?
WebApi does not support multiple parameter binding from a post request. You can check more details here.
So the proper way for the WebApi is to create a request model that will contain all the properties that will be bound. Perhaps you can try multiple [FromUri] parameters, but then you will have to add them to the url yourself in angualr, rather than just pass to .post.
Example model:
public class RequestModel
{
public string Url {get;set;}
public string Name {get;set;}
}
I also believe that adding the model improves the structure of your code as you always know what your server expects rather than working with some dynamic data.
P.S. Did not notice that you use ASP.Net Core, my data is from web api 2, but perhaps it's still valid, so you will need to create a model + FromBody should not be required on post requests since it's the default behavior.
I think your controller is wrong. You are trying to pass a Url and a name whereas your controller method is waiting for a single Url.
Try to pass only a Url and it should work.
If you want to pass the Url and the Firstname, you have to modify your controller method like this :
[HttpPost]
public string Post([FromBody] string Url, string FirstName)
{
// Do whatever you need to do here ...
}

Categories