What the difference between [FromRoute] and [FromBody] in a Web API? - asp.net

What the difference between [FromRoute] and [FromBody] in a Web API?
[Route("api/Settings")]
public class BandwidthController : Controller
{
// GET: api/Settings
[HttpGet]
public IEnumerable<Setting> GetSettings()
{
return _settingRespository.GetAllSettings();
}
// GET: api/Settings/1
[HttpGet("{facilityId}", Name = "GetTotalBandwidth")]
public IActionResult GetTotalBandwidth([FromRoute] int facilityId)
{
if (!ModelState.IsValid)
{
return HttpBadRequest(ModelState);
}
}
}
Also for PUT:
// PUT: api/Setting/163/10
[HttpPut]
public void UpdateBandwidthChangeHangup([FromRoute] int facilityId, int bandwidthChange)
{
_settingRespository.UpdateBandwidthHangup(facilityId, bandwidthChange);
}
Can I use [FromBody]?

FromBody
Specifies that a parameter or property should be bound using the request body.
When you use FromBody attribute you are specifying that the data is coming from the body of the request body and not from the request URL/URI. You cannot use this attribute with HttpGet requests, only with PUT,POST,and Delete requests. Also you can only use one FromBody attribute tag per action method in Web API (if this has changed in mvc core I could not find anything to support that).
FromRouteAttribute
Summary: Specifies that a parameter or property should be bound using route-data from the current request.
Essentially it FromRoute will look at your route parameters and extract / bind the data based on that. As the route, when called externally, is usually based on the URL. In previous version(s) of web api this is comparable to FromUri.
[HttpGet("{facilityId}", Name = "GetTotalBandwidth")]
public IActionResult GetTotalBandwidth([FromRoute] int facilityId)
So this would try to bind facilityId based on the route parameter with the same name.
Complete route definition: /api/Settings/GetTotalBandwidth/{facilityId}
Complete received url: /api/Settings/GetTotalBandwidth/100
Edit
Based on your last question, here is the corresponding code assuming you want 163 to be bound to facilityId and 10 to bandwidthChange parameters.
// PUT: api/Setting/163/10
[HttpPut("{facilityId}/{bandwidthChange}")] // constructor takes a template as parameter
public void UpdateBandwidthChangeHangup([FromRoute] int facilityId, [FromRoute] int bandwidthChange) // use multiple FromRoute attributes, one for each parameter you are expecting to be bound from the routing data
{
_settingRespository.UpdateBandwidthHangup(facilityId, bandwidthChange);
}
If you had a complex object in one of the parameters and you wanted to send this as the body of the Http Request then you could use FromBody instead of FromRoute on that parameter. Here is an example taken from the Building Your First Web API with ASP.NET Core MVC
[HttpPut("{id}")]
public IActionResult Update([FromRoute] string id, [FromBody] TodoItem item);
There are also other options in MVC Core like FromHeader and FromForm and FromQuery.

Confusion with [FromRoute] versus [FromBody]
Microsoft has done a good job with ASP.NET trying to connect Internet technology into their frameworks and systems. But in the process they often confuse developers by trying to patch their technology solutions on top of what are very simple HTTP and HTML technologies. People walk away assuming their solutions represent how the World Wide Web works...when it simply does not. Routing URL solutions in both their .NET Framework and Core version of ASP.NET are still confusing to many.
Hopefully this helps...
When you create a basic "route" in ASP.NET Core, you are binding your your browser's URL Address Path to a specific Controller Class and its child Method() inside a ASP.NET compiled application. Binding means that Middleware, they designed, sniffs the URL address and tries to take it apart and then match the pieces to code inside your website.
Special Controller-ControllerBase classes handle this process inside ASP.NET using Conventional Routing Templates and Routing Attributes mapped to child Methods inside your classes. That includes the parameters of those methods. This then "routes" all URL browser address path requests to a specific set of code inside your web application in ASP.NET.
Unfortunately, there are too many ways to configure this binding. In the past, it was a bunch of random combinations of Route Templates to controllers & methods, with additional code to map a url to parameters. But that was not always clear for things like query strings. ASP.NET Core has tried to clean up that mess with these new Attribute Routing decorations like [FromRoute], etc.
So how do they work?
[Route("/mypath/{myid}"]
public string MyMethod(int myid){...}
The above matches this browser URL address on the Web:
http://example.com/mypath/5
In its simplest form, ASP.NET MVC or WebAPI uses a "route" text string to map a specific method parameter name to a matching URL path. [FromRoute] helps by binding explicitly a methods parameter name to a matching part of the URL in {}:
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpGet("{facilityId}")]// "/api/test/26"
public IActionResult GetTotalBandwidth([FromRoute] int facilityId)
{
...
}
}
The [FromRoute] attribute decoration, however, is optional as Middleware in ASP.NET by default tries to map that for you out of the box. If you leave [FromRoute] off, it will try and map your parameter name to the route template name inside {}, as so:
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpGet("{facilityId}")]// "/api/test/26"
public IActionResult GetTotalBandwidth(int facilityId)
{
...
}
}
[FromQuery], however, only binds to Querystring URL parameters, which lie outside the URL route, according to Microsoft. But I have always considered them as part of the URL. In past iterations of ASP.NET this was left out so people had to fudge a way to grab these values inside the method using Request.QueryString. So this new feature fixes this issue:
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpGet("{facilityId}")]// "/api/test/26?num=5"
public IActionResult GetTotalBandwidth(int facilityId,[FromQuery] int num)
{
...
}
}
[FromBody] is Completely Different!
Capturing data from a POST has always been tricky. ASP.NET helps solve that by allowing the route URL mapping system to function as normal in listening for the POST and URL request, but using an additional method parameter attribute system to bind to that data coming in using [FromBody]. In the case of POST HTTP VERB types, POST is unique, as a POST has nothing to do with URL mapping. But in this case ASP.NET is using the attribute parameter binding system to grab that form field data for you.
Let's first see how POST form fields work.
When you send HTML form data using a "method=post" like so:
<!doctype html>
<html xml:lang="en-us" lang="en-us">
<head></head>
<body>
<form id="f1" name="f1" method="post" action="">
<input type="text" id="field1" name="field1" size="20" value="" />
<button id="mybutton" name="mybutton" value="submit">Submit</button>
</form>
</body>
</html>
...that sends the form field data packaged inside the "body" of the Request like so as a POST:
HTTP HEADER
Content-Type: text/html
Last-Modified: Fri, 27 Jan 2023 17:45:32 GMT
HTTP BODY
field1=hello&mybutton=submit
Notice the form field POST data is inside a special "body" or payload section of the request post (field1=hello&mybutton=submit). To get that data, you need to use [FromForm](for traditional name-value form POST data) or [FromBody] (special JavaScript JSON Post data only) in ASP.NET parameter decorations, like so:
// Note: In the example below, "field1" only captures one field.
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpPost]// "/api/test"
public string MyPostMethod([FromForm] string field1)
{
...
}
}
[FromForm] captures regular HTML POST form fields (one field or all fields) as name-value pairs in the WebAPI POST endpoint.
[FromBody] captures the form fields but only as JSON data, requiring an additional content-type of 'application/json' added to the POST request sent to the server. Because HTML Forms cannot do that, you have to use JavaScript when using [FromBody].
So, [FromBody] is almost always used with a POST and has nothing to do with binding a Route Template as part of the URL like [FromRoute] or [FromQuery] might do, but simply captures any POST data typically found inside a POST request form submission sent inside the Http Request package.
Hope that helps!

Related

Delete WebApi FromURI binding

I am trying to create a .NET5 WebApi delete method in a controller class where this method receives several "ids" that will be used for deleting some entities.
I realized when building the delete request on the client side that specifying a content does not make sense. So I was guided to pass ids on the Uri, hence the use of the "FromUri" attribute:
// DELETE: api/ProductionOrders/5
[HttpDelete("ProductionOrders")]
public IActionResult DeleteProductionOrder([System.Web.Http.FromUri]int[] ids)
{
//code
}
If this is a reasonable approach, is there a better way to build this Uri from the client-side? Imagine instead of an array of ints I had a complex type. How can I serialized this and put into the Uri?
For this example I end up building up a URI like this:
http://localhost:51081/api/ProductionOrders?ids=25563&ids=25533
Personally, if I have to pass a List or a complex type I would map values from the Body via JSON. The DELETE allow using body. And then just decorate your param with [FromBody] attribute.
Despite some recommendations not to use the message body for DELETE requests, this approach may be appropriate in certain use cases.
This allows better extensibility in case you need to change how the data is coming.
In your case with ids I’d create new class like this:
public class RequestEntity {
[JsonPropertyName("Ids")]
public List<int> Ids { get; set; }
}
And then when calling this method, send the Body along with the request.
{
"Ids": [25392, 254839, 25563]
}
In a future you can pass complex objects just by changing what is send to server and implement complex logic.

Spring mvc: is there a way to call another controller, get the response and fill it to a model attribute?

I have two mvc style endpoints returning templatefile names as view names, in the same classpath: /source and /target. The source_template has a variable which needs to be filled by contents of another template, say target_template.
#RestController
class SomeController {
#GetMapping("/source")
public String source(Model model) {
model.addAttribute("attr1", /*call endpoint /target and add the response of parsed template 'target_template' here */);
return "source_template";
}
#GetMapping("/target")
public String target(Model model) {
model.addAttribute("attr2", "good");
//may be continue the nested invocation n number of times
return "target_template";
}
}
given the source_template.html:
Hai, $attr1
and the target_template.html:
this has been a $attr2 day
having said, i invoke the url /source, I should get "Hai, this has been a good day".
I can just call the target() method directly, but that would not render the template. Or I should directly use the templating engine apis to link the template file, put the context object, parse the template and return the string , which defeats the whole purpose of spring mvc. Or I can use resttemplate, but that requires an absolute url, and performance would take a hit. So, is there any other way to do it ?

How to send integer array as a parameter from endpoint without querystring from url in asp.net core webapi

I have an endpoint that has a parameter that type of integer array.I want send some value from body as a json string.When I tried it , I was getting null value from parameter so I tried to send integer array from url but happen problem about of url length so I want to know that is possible to send it from request body or how to fix url length problem for 5.000 item
request body that I tried
{
"Ids": [349]
}
endpoint function
[HttpGet]
public void GetModels([FromBody]List<int> Ids)
{
}
First, in general including a body in a GET request is often not considered very RESTful. It is no longer specifically "banned" by RFC, but it is not typical. That said, you can make this work in ASP.Net Core using the [FromBody] attribute.
The issue has to do with how you are formatting your JSON body. Using the signature for GetModels that you have listed above, the JSON body doesn't match the parameters. Your JSON represents a top-level object with a property Ids that is an array of int, not just an array of it (or List).
If you want to use public void GetModels([FromBody]List<int> Ids) then your JSON body should simply be an array (e.g. [349,350,351]) and nothing else (no brackets, no "Ids" property name).
If you want to use the JSON body you list above, then you need another class to use for model binding, a DTO. That DTO would look something like:
public class IdDto
{
public List<int> Ids { get; set; }
}
and then your GetModels method would look like:
[HttpGet]
public void GetModels([FromBody] IdDto idDto)
{
var myIds = idDto.Ids;
}
Lastly, be sure that your GET request has a Content-Type set to application/json or ASP.Net will return a 415 "Unsupported Media Type".

Alternative to Server.Transfer in ASP.NET Core

I am migrating an ASP.NET application to ASP.NET Core and they have some calls to HttpServerUtility.Transfer(string path). However, HttpServerUtility does not exist in ASP.NET Core.
Is there an alternative that I can use? Or is Response.Redirect the only option I have?
I want to maintain the same behaviour as the old application as much as possible since there is a difference in between Server.Transfer and Response.Redirect.
I see some options for you, depending on your case:
Returning another View: So just the HTML. See answer of Muqeet Khan
Returning another method of the same controller: This allows also the execution of the business logic of the other action. Just write something like return MyOtherAction("foo", "bar").
Returning an action of another controller: See the answer of Ron C. I am a bit in troubles with this solution since it omits the whole middleware which contains like 90% of the logic of ASP.NET Core (like security, cookies, compression, ...).
Routing style middleware: Adding a middleware similar to what routing does. In this case your decision logic needs to be evaluated there.
Late re-running of the middleware stack: You essentially need to re-run a big part of the stack. I believe it is possible, but have not seen a solution yet. I have seen a presentation of Damian Edwards (PM for ASP.NET Core) where he hosted ASP.NET Core without Kestrel/TCPIP usage just for rendering HTML locally in a browser. That you could do. But that is a lot of overload.
A word of advice: Transfer is dead ;). Differences like that is the reason for ASP.NET Core existence and performance improvements. That is bad for migration but good for the overall platform.
You are correct. Server.Transfer and Server.Redirect are quite different. Server.Transfer executes a new page and returns it's results to the browser but does not inform the browser that it returned a different page. So in such a case the browser url will show the original url requested but the contents will come from some other page. This is quite different than doing a Server.Redirect which will instruct the browser to request the new page. In such a case the url displayed in the browser will change to show the new url.
To do the equivalent of a Server.Transfer in Asp.Net Core, you need to update the Request.Path and Request.QueryString properties to point to the url you want to transfer to and you need to instantiate the controller that handles that url and call it's action method. I have provided full code below to illustrate this.
page1.html
<html>
<body>
<h1>Page 1</h1>
</body>
</html>
page2.html
<html>
<body>
<h1>Page 2</h1>
</body>
</html>
ExampleTransferController.cs
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace App.Web.Controllers {
public class ExampleTransferController: Controller {
public ExampleTransferController() {
}
[Route("/example-transfer/page1")]
public IActionResult Page1() {
bool condition = true;
if(condition) {
//Store the original url in the HttpContext items
//so that it's available to the app.
string originalUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}{HttpContext.Request.QueryString}";
HttpContext.Items.Add("OriginalUrl", originalUrl);
//Modify the request to indicate the url we want to transfer to
string newPath = "/example-transfer/page2";
string newQueryString = "";
HttpContext.Request.Path = newPath;
HttpContext.Request.QueryString = new QueryString(newQueryString);
//Now call the action method for that new url
//Note that instantiating the controller for the new action method
//isn't necessary if the action method is on the same controller as
//the action method for the original request but
//I do it here just for illustration since often the action method you
//may want to call will be on a different controller.
var controller = new ExampleTransferController();
controller.ControllerContext = new ControllerContext(this.ControllerContext);
return controller.Page2();
}
return View();
}
[Route("/example-transfer/page2")]
public IActionResult Page2() {
string originalUrl = HttpContext.Items["OriginalUrl"] as string;
bool requestWasTransfered = (originalUrl != null);
return View();
}
}
}
Placing the original url in HttpContext.Items["OriginalUrl"] isn't strictly necessary but doing so makes it easy for the end page to know if it's responding to a transfer and if so what the original url was.
I can see this is a fairly old thread. I don't know when URL Rewriting was added to .Net Core but the answer is to rewrite the URL in the middleware, it's not a redirect, does not return to the server, does not change the url in the browser address bar, but does change the route.
resources:
https://weblog.west-wind.com/posts/2020/Mar/13/Back-to-Basics-Rewriting-a-URL-in-ASPNET-Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0
I believe you are looking for a "named view" return in MVC. Like so,
[HttpPost]
public ActionResult Index(string Name)
{
ViewBag.Message = "Some message";
//Like Server.Transfer() in Asp.Net WebForm
return View("MyIndex");
}
The above will return that particular view. If you have a condition that governs the view details you can do that too.
I know that this is a very old question, but if someone uses Razor Pages and is looking to a Server.Transfer alternative (or a way to return a different view depending on a business rule), you can use partial views.
In this example, my viewmodel has a property called "UseAlternateView":
public class TestModel : PageModel
{
public bool UseAlternateView { get; set; }
public void OnGet()
{
// Here goes code that can set UseAlternateView=true in certain conditions
}
}
In my Razor View, I renderize a diferent partial view depending of the value of the UseAlternateView property:
#model MyProject.Pages.TestModel
#if (Model.UseAlternateView)
{
await Html.RenderPartialAsync("_View1", Model);
}
else
{
await Html.RenderPartialAsync("_View2", Model);
}
The partial views (files "_View1.cshtml" and "_View2.cshtml"), contain code like this:
#model MyProject.Pages.TestModel
<div>
Here goes page content, including forms with binding to Model properties
when necessary
</div>
Obs.: when using partial views like this, you cannot use #Region, so you may need to look for an anternative for inserting scripts and styles in the correct place on the master page.

How can ASP.NET MVC enhance security compared to a webform?

Since we have separation of layers it should be easier to isolate each layer by security. How does MVC in ASP.NET exploit this to more easily secure a website compared to using a webform?
By security I do not only mean authorization but also anti-hacking security.
1) Decorate Controller/Action by [Authorize] attribute (optionnaly with list of roles that allowed).
Example:
[Authorize("Manager")]
public class MyController:Controller
{
//Each action available only for authorized user
[Authorize(Roles = "Admin;Customer")]
public ActionResult MyAction()
{
//This action is available to the user that have a Manager role and one of next roles: Admin, Customer.
}
public ActionResult AnotherAction()
{
// This action is available for managers only.
}
}
2) Create area at your MVC project, create base controller, that decorated by authorize attribute, and make all controllers at area inherited from decorated controller - that's way you can easy implement admin area for website, for example.
Example:
[Authorize(Roles = "Admin")]
public class BaseController: Controller
{
}
public class My2Controller: BaseController
{
public ActionResult DoSomething()
{
//This action is available only for Admin.
}
}
If you need to protect site from HTML/script injection (user input ... at text field 'surname', that will collect data from admin page 'all site users' and post to his site when you open your page) - you can use next methods.
do nothing - by default MVC validates input, and don't process requests with HTML tags and returns error
use validation - for many kinds of data (names, phones, dates) user should not have possibility to use '<' and '>'. but you don't want user see error page if makes accidental mistake. Than enable client validation.
Use attribute [ValidateInput(false)] and encode incoming data - but user can't input HTML if he really need it.
If you really need user can format his message - use HTML filters for giving possibility input tags from list of permitted. As example: http://htmlagilitypack.codeplex.com/
Provide your own formatting dialect that will be safe and will not use HTML tags
If you need to protect from cross site fake form posting - use [ValidateAntiForgeryToken] attribute with #Html.AntiForgeryToken(); inside your <form> tag. This method generates hidden input with random value and cookie with known key and same value, and attribute check if form contains value, cookie contains value and form value equals to cookies value.
You can create your own security rules, using a bunch of attribute and hidden input on form: for example to set form time-outs.
You can put authorize attributed on each function call with role requirements.
One way users can hack your site is through "Cross-Site Request Forgery" and you can help prevent it by using AntiForgeryToken().
// Your View
#using (Html.BeginForm("Register", "Account"))
{
// Your Form Fields
#Html.AntiForgeryToken("someSalt")
}
// Your Controller
[HttpPost, ValidateAntiForgeryToken(Salt = "someSalt")]
public ActionResult Register(MyViewModel viewModel)
{
// Your Code
}

Resources