I have a WebAPI method which takes in 3 parameters, two of them primitive data types and the 3rd one is a complex data type:
public HttpResponseMessage validateUser(string elementName, string checkPermission, List<AccessElement> accessGroups)
I'm making call to this WebAPI using Angular $http:
return $http({
method: 'get',
url: serviceUrlPrefix + '/api/v1/validateUser',
params: { 'elementName': CONSTANTS.UNAUTH_DATA_UI, 'checkPermission': CONSTANTS.CAN_READ , 'accessGroups': accessGroups }
})
Problem:
When $http request is made, the query string values are truncated as the complex datatype parameter accessGroups is relatively long. I read in one of the blogs that query string limit in IE is 2083 characters
Question 1:
In my scenario, I won't say that acessGroups object is too big as it is a collection of 10 records (having 4 columns). With the query string limit on each browser, its quite understandable that I would face this truncation very easily when we pass the complex data type. So if it is the case, I would like to understand what is the primary use of [FromUri].
Question 2:
I was able to get around this issue by making the controller method as
public HttpResponseMessage validateUser(string elementName, string checkPermission, [FroimUri]List<AccessElement> accessGroups)
And made a POST call to this method with "data" parameter
return $http({
method: 'post',
url: serviceUrlPrefix + '/api/v1/validateUser',
params: { 'elementName': CONSTANTS.UNAUTH_DATA_UI, 'checkPermission': CONSTANTS.CAN_READ },
data: { 'accessGroups': accessGroups }
})
What is the drawback in this approach, since I'm going for "POST" method from a normal "Get". Would it cause any additional overhead?
I believe the only drawback is not adhering to the REST design principles (POST should be used to create a resource according to REST paradigm).
In practical terms, I would use [FromBody] tag. That way, you can be certain you won't run into any length restrictions.
Related
In ASP.Net Core you have multiple ways to generate an URL for controller action, the newest being tag helpers.
Using tag-helpers for GET-requests asp-route is used to specify route parameters. It is from what I understand not supported to use complex objects in route request. And sometimes a page could have many different links pointing to itself, possible with minor addition to the URL for each link.
To me it seems wrong that any modification to controller action signature requires changing all tag-helpers using that action. I.e. if one adds string query to controller, one must add query to model and add asp-route-query="#Model.Query" 20 different places spread across cshtml-files. Using this approach is setting the code up for future bugs.
Is there a more elegant way of handling this? For example some way of having a Request object? (I.e. request object from controller can be put into Model and fed back into action URL.)
In my other answer I found a way to provide request object through Model.
From the SO article #tseng provided I found a smaller solution. This one does not use a request object in Model, but retains all route parameters unless explicitly overridden. It won't allow you to specify route through an request object, which is most often not what you want anyway. But it solved problem in OP.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Context.GetQueryParameters()" asp-route-somestring="optional override">Link</a>
This requires an extension method to convert query parameters into a dictionary.
public static Dictionary GetQueryParameters(this HttpContext context)
{
return context.Request.Query.ToDictionary(d => d.Key, d => d.Value.ToString());
}
There's a rationale here that I don't think you're getting. GET requests are intentionally simplistic. They are supposed to describe a specific resource. They do no have bodies, because you're not supposed to be passing complex data objects in the first place. That's not how the HTTP protocol is designed.
Additionally, query string params should generally be optional. If some bit of data is required in order to identify the resource, it should be part of the main URI (i.e. the path). As such, neglecting to add something like a query param, should simply result in the full data set being returned instead of some subset defined by the query. Or in the case of something like a search page, it generally will result in a form being presented to the user to collect the query. In other words, you action should account for that param being missing and handle that situation accordingly.
Long and short, no, there is no way "elegant" way to handle this, I suppose, but the reason for that is that there doesn't need to be. If you're designing your routes and actions correctly, it's generally not an issue.
To solve this I'd like to have a request object used as route parameters for anchor TagHelper. This means that all route links are defined in only one location, not throughout solution. Changes made to request object model automatically propagates to URL for <a asp-action>-tags.
The benefit of this is reducing number of places in the code we need to change when changing method signature for a controller action. We localize change to model and action only.
I thought writing a tag-helper for a custom asp-object-route could help. I looked into chaining Taghelpers so mine could run before AnchorTagHelper, but that does not work. Creating instance and nesting them requires me to hardcode all properties of ASP.Net Cores AnchorTagHelper, which may require maintenance in the future. Also considered using a custom method with UrlHelper to build URL, but then TagHelper would not work.
The solution I landed on is to use asp-all-route-data as suggested by #kirk-larkin along with an extension method for serializing to Dictionary. Any asp-all-route-* will override values in asp-all-route-data.
<a asp-controller="Test" asp-action="HelloWorld" asp-all-route-data="#Model.RouteParameters.ToDictionary()" asp-route-somestring="optional override">Link</a>
ASP.Net Core can deserialize complex objects (including lists and child objects).
public IActionResult HelloWorld(HelloWorldRequest request) { }
In the request object (when used) would typically have only a few simple properties. But I thought it would be nice if it supported child objects as well. Serializing object into a Dictionary is usually done using reflection, which can be slow. I figured Newtonsoft.Json would be more optimized than writing simple reflection code myself, and found this implementation ready to go:
public static class ExtensionMethods
{
public static IDictionary ToDictionary(this object metaToken)
{
// From https://geeklearning.io/serialize-an-object-to-an-url-encoded-string-in-csharp/
if (metaToken == null)
{
return null;
}
JToken token = metaToken as JToken;
if (token == null)
{
return ToDictionary(JObject.FromObject(metaToken));
}
if (token.HasValues)
{
var contentData = new Dictionary();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToDictionary();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary { { token.Path, value } };
}
}
I have the following code:
[HttpPost]
public async Task<ReturnStatus> Delete([FromBody]int id)
{
await new BusinessLogic.Templates().DeleteTemplate(id);
return ReturnStatus.ReturnStatusSuccess();
}
When I run this as an AJAX request, the id is null. I've inspected the data coming in through Fiddler and the body is:
{"id":"11"}
The header has Content-Type: application/json; charset=UTF-8.
If I modify the code slightly to
[HttpPost]
public async Task<ReturnStatus> Delete([FromBody]string id)
{
await new BusinessLogic.Templates().DeleteTemplate(Convert.ToInt64(id));
return ReturnStatus.ReturnStatusSuccess();
}
it works just fine.
What am I doing wrong here?
Please read this part, number 3 in particular:
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
3. [FromBody] parameters must be encoded as =value
(quoting the section for future reference:)
There are two ways to make jQuery satisfy Web API’s encoding requirement. First, you can hard code the = in front of your value, like this:
$.post('api/values', "=" + value);
Personally, I’m not a fan of that approach. Aside from just plain looking kludgy, playing fast and loose with JavaScript’s type coercsion is a good way to find yourself debugging a “wat” situation.
Instead, you can take advantage of how jQuery encodes object parameters to $.ajax, by using this syntax:
$.post('api/values', { '': value });
I am trying to do a domain availability search using an API from free domain API.
After i create an account, it shows:
**Make a REST request using this URL:**
http://freedomainapi.com/?key=11223344&domain=freedomainapi.com
And looking in the documentation page, it has only:
Request http://freedomainapi.com?key=YOUR_API_KEY&domain=DOMAIN_NAME
Result:
{
"status": "success",
"domain": "freedomainapi.com",
"available": false
}
I am very new to APIs...
What I need is to show a domain search box, and when the user enters, it should return with result.
It claims to show domain suggestions as well. I hope it will also work.
Using jquery and a jsonp proxy
http://jsfiddle.net/mp8pukbm/1/
$.ajax({
type: 'GET',
url: "https://jsonp.nodejitsu.com/?callback=?",
data: {url: 'http://freedomainapi.com?key=14ejhzc5h9&domain=freedomainapi.com'},
dataType: "jsonp",
success: myfn
});
function myfn(data) {
console.log(data);
}
you have to use the proxy because cross domain json is not permitted
EDIT:
i made an update to show the result in a div (stringified)
http://jsfiddle.net/mp8pukbm/2/
EDIT #2: i created a test key on that site, you have to use your own
EDIT #3: and there's your combo: http://jsfiddle.net/mp8pukbm/4/
Assuming that you will use java script for showing the search box, you can use AJAX feature of java script (or jQuery or Dojo) ... All you need to do is a "GET" request that like you can pasted and you will get the result back on the response object. To try out the API you can use "Postman" application in Chrome. https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en
In the response object of the AJAX call you will get a JSON object which you can parse and display the result.
Normally when we use REST we need to differentiate one REST call from another.
Assuming this url
http://freedomainapi.com/checkAvailability?key=YOUR_API_KEY&domain=DOMAIN_NAME
In Application layer we need to write an interface
#GET
#Path("/checkAvailability")
#Produces({MediaType.APPLICATION_JSON})
public ReturnObject getDomainAvailability(#QueryParam("key") String key,
#QueryParam("domain") String doaminName );
Once interface is done you need to write your implementation class.
This class will intract with business layer and perform search task and based on
result collected will create ReturnObject.
ReturnObject => will contain status, domain and availability
On screen
$.ajax({
type: "GET",
url: 'root/checkAvailability',
success: function(jsonData)
{
// read json and perform operation
}
,
error: function (error)
{
// handle error
}
});
If you are using JAVA as backend then you can use gson to parse the result, which is a json. After parsing you can read the values from result and display accordingly :)
Any API is a way to extend a given software. (Might be a website or an application)
In both ways there is a certain way to communicate with the software. In your example freedomainapi.com allows you to fetch if given domain is avaiable. There is no such thing as a suggestion tho, atleast i cannot find any suggestions at all.
Given output is a message format know as JSON. It can be easily interpreted by many major Languages such as Java, Javascript and PHP.
Given String might be easily interpreted as a Map consisting of a status (String), a domain (string) and avaiable (boolean)
A domain availability search could not be easier, assuming K is your key, D is your search input (Domain):
Download http://freedomainapi.com/checkAvailability?key=K&domain=D as input
Parse JSON from input as json
return json["status"] == "success" and json["avaiable"]
Depending on your language you might need to use methods to access properties of json, but that does not influence the basic usage of this api.
on user enters, it calls click_button function and I am assuming your result displaying div id is "main_container" you can give domain suggestions by passing related DOMAIN_NAME s as arguments to click_button function
function click_button(DOMAIN_NAME){
$.ajax({
url : 'http://freedomainapi.com?key=YOUR_API_KEY&domain=DOMAIN_NAME',
type: 'GET',
crossDomain: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
data=JSON.parse(data);
if(data['available']){
$('#main_container').html($('#main_container').html()+'<br>'+DOMAIN_NAME+': Available');
else{
$('#main_container').html($('#main_container').html($('#main_container').html()+'<br>'+DOMAIN_NAME+': Not Available');
}//success
});//ajax
}
hope it helpful !
I'm not confident that the title is well put so feel free to criticize. :)
I have a controller that returns a page on which the user can click on some options creating a list of items (dynamically built up on the client using JS). As the user is satisfied, they can click on a button and then...
...currently, some DIVs are hidden/displayed, converting the (same) page to a read-only viewer of the selection.
...optimally, another ActionResult should be invoked presenting the information.
My biff is that I can't decide on a good way to transfer the data from one page to another: query string is one option, storing/retrieving to/from DB is an other. I'm not happy with any of these.
What would be a smooth and recommended way to transfer the data to a new view withing the same controller?
$.ajax({
url: '#(Url.Action("Action", "Controller"))',
type: 'post',
data: {
id: id,
data1: data1
},
success: function (result) {
if (result.Success) {
}
});
A very simple way is like the above where you define as many fields as you want and as long as the input parameters match they will be received on the controller
public ActionResult Action(string id, string data1){...
if you want to get more complicated you can build lists and arrays with the json data and then it is usually a good idea to stringify it.
var data = {};
data.id = 'id';
data.list = [];
data.list.push({ name: 'name', location: 'location', etc })
then in the ajax call
data: Json.stringify(data),
again, as long as the names match the controller will receive it. Hope this helps
Edit:
Json.stringify is a tool that is used for sending the data. I don't know all of the details of what it does but it is recommended to use for more complex data. The example here I used for sending a model back to the controller but you mentioned not wanting to create a model. I believe to receive this data on the controller side you need to have input parameters that match what is defined in data. From what I have above list is a complex type so your controller would be something like this.
Public ActionResult Action(string id, List<ComplexType> list){...
We have a form that displays user information for a list of users. We have an action link to go to a details view for the user to update information. Our application is a mixture of ASP.Net 4.0 and MVC. We normally use encryption to mask variables we use in the query string, but MVC chokes when we attempt to encrypt the query string. We are using the Microsoft Enterprise 5.0 Cryptogrophy class.
How would we go about either encrypting the query string or passing the data without using the query string at all?
We are using MVC 4 with Razor.
We are currently doing something like this:
#Url.Action("Edit", "User", new {id = user.Id}
BTW, I am new to MVC, so there may be an easy answer to this that I am just not aware of.
It would be really nice if we could not use the query string at all.
The simple answer: POST. Use a form or an AJAX call to send the values as POST data, and have a controller method named the same as the existing one, but marked with the [HttpPost] attribute (also, mark the existing one as [HttpGet]). The arguments for this new POST method can be whatever you like; they may get turned into strings when they get POSTed (especially if you use AJAX) but MVC is smart enough to convert them back again provided they're named the same. So, a form that's something like this:
#using (Html.BeginForm("Edit", "User", FormMethod.Post, new { id = "mainForm" }))
{
<input id="userId" type="text" />
<input type="submit" />
}
will correspond neatly to a controller method like this:
[HttpPost]
public ActionResult Edit(int userId)
{
//do whatever
}
provided you've got the routes registered properly. If your GET method is working, then the same route will work here, as long as it doesn't do anything problematic with URL parameters.
That same method will also accept the submission from an AJAX call that looks something like this:
$.ajax({
type: "POST",
url: '#Url.Action("Edit", "User")',
data: { userId: #user.Id },
success: function (data) {
//do whatever
}
});
I agree with anaximander, but if you're going to that much effort to secure the query string information, forms just move the information to HTML fields instead of query string parameters. If you need to keep your existing implementation you could look into inheriting from the default ModelBinder, and provid your own custom implementation to convert from encrypted query string to unencrypted query string before you hand it over to the base class's implementation.