JavaScript in C# ASP MVC issue - c#

We have a web project that takes data from an MS SQL database and uses the Google Visualisation API to display these charts on the web view.
Recently we have added castle windsor so we can configure the application to different users with an XML file. Before we added this, the view worked fine, using the baked in parameters that were needed for this query. For some reason, when we send in the parameters from the XML files (Running with breakpoints shows that the parameters are being passed to the main controller action for the page) the data isn't being returned. here is some of the code for you.
JavaScript
<script type="text/javascript">
var csvDataUrl = '#Url.Action("TradeValuesDataCsv", "Dashboard")';
var jsonDataUrl = '#Url.Action("TradeValuesDataJson", "Dashboard")';
google.load("visualization", "1", { packages: ['table', 'corechart', 'gauge'] });
google.setOnLoadCallback(drawCharts);
drawCharts();
$("body").on({
ajaxStart: function () {
$(this).addClass("loading");
},
ajaxStop: function () {
$(this).removeClass("loading");
}
});
function drawCharts() {
var queryString = 'platform=' + $('#PlatformDropDownList').val();
queryString += '&startDate=' + $('#startDatePicker').val();
queryString += '&endDate=' + $('#endDatePicker').val();
queryString += '&model=' + $('#ModelDropDownList').val();
queryString += '&eventType=' + '#Model.EventType';
queryString += '&parameterName=' + '#Model.ParameterName';
$.ajax({
type: "POST",
url: jsonDataUrl,
data: queryString,
statusCode: {
200: function (r) {
drawToolbar(queryString);
drawTable(r);
drawChart(r);
},
400: function (r) {
},
500: function (r) {
}
}
});
}
Main controller Method for this page:
public ActionResult ActionResultName(EventTypeParameterNameEditModel model)
{
var viewModel = new EventTypeParameterNameViewModel(_queryMenuSpecific);
viewModel.EventType = model.EventType;
viewModel.ParameterName = model.ParameterName;
PopulateFilters(viewModel);
return this.View(viewModel);
}
Retrieve the JSON Data Controller Method:
public ActionResult ActionResultNameJson(EventTypeParameterNameEditModel filters)
{
List<CustomDataType> results = this.GetTradeValues(filters);
return this.Json(results, JsonRequestBehavior.AllowGet);
}
EDIT I have managed to find a solution, even if it is a rather messy one. I have some filters built into the page that allow the user to filter by device and by OS, and these were being populated on the page load with 'undefined'. I didn't spot this first time round with NHProf Running, but this wasn't happening when the page loaded before we configured the input to be from XML. I will add this as an answer and accept it and close the question. Thanks everyone for your attempts to help. Starting to really like this community. Perfect place to find help as a Graduate Developer.

Yep. I'm not a Razor syntax expert but I think these property references are probably your problem. I suspect razor is going to tend to avoid asserting itself inside strings being used in statements with properties in JS contexts. Or you could try implementing as getter functions which would probably work. Otherwise an # and a . in a string could easily lead to confusing mixups with email addresses when it's not an obvious method call:
queryString += '&eventType=' + '#Model.EventType';
queryString += '&parameterName=' + '#Model.ParameterName';
As a general rule in any server to client-side scenario, my advice is to confine JavaScript direct from the back end to JSON objects only. That way you have more granular control over what's going on on both sides of the http request wall and your client-side devs don't have to figure where stuff is getting built if there's a short-term need to quickly modify. In general, don't build behavioral code with other code if you can avoid it.
I couldn't convince my .net MVC boss at first but he slowly came around to the idea on his own months later.
We also store a URL base path along with some other context-shifting params in a standard JSON object that loads on every page so the JS devs can add these things linked JS files rather than have to work with JS in the HTML (I don't recall why but document.location wasn't always going to work).
Lastly, try to keep the JS out of the HTML. Link it. It seems like a pain from a procedural POV but trust me. It makes life much easier when you are juggling 3 major concerns as one ball each rather than all in the same jumbled HTML/template mess.

It turned out that the problem was not in my Javascript. I have some filters in there that allow the user to filter the results my model and operating system and date and what not. These were being automatically populated on page load with 'undefined' which is not an option in the database. I added something to catch that in the call to the query and it seemed to solve the problem.

Related

Internet Explorer redirect on all .Net Core project pages

I'm a .Net Core beginner and look for a solution to protect the application from IE.
I got the following code working in Controller:
string userAgent = Request.Headers["User-Agent"].ToString();
var flagsIE = new [] {"MSIE", "Trident"};
if(flagsIE.Any(userAgent.Contains)){
return RedirectToAction("BadBrowser");
}
Now if someone is trying to visit the page using IE, they get redirected to an info-page asking them to use a modern browser. Instead of adding this code to every single Controller.cs I would like to add it on project level, so that it get's executed independent from the location within of the project.
And, I know that this can be achieved with _Layout.cshtml, my question is:
Where and how would I implement a function to get it executed for all requested views withing the project without adding it every single request within Controller.
Thank you in advance.
As Icepickle suggested, in the comments of my question, I used middleware in Startup.cs and got the desired result:
app.Use(async (context,next) =>
{
string userAgent = context.Request.Headers["User-Agent"].ToString();
var IEkeywords = new [] {"MSIE", "Trident"};
if (IEkeywords.Any(userAgent.Contains)){
context.Response.Redirect("/IE/redirect/badbrowser.html");
return;
}
await next();
});
You can try to add js in _Layout.cshtml to detect the browser is IE:
<script>
var isIE = false || !!document.documentMode;
if (isIE) {
window.location.href = "/Home/ForbidInfo";
}
</script>
Then create a view named ForbidInfo under Home controller which to show the message to user that he cannot use the IE to show views.
(Notes: this view's Layout must be null ,or it will enter an endless loop)
#{
ViewData["Title"] = "ForbidInfo";
Layout = null;
}
<h1>Please use a modern browser!</h1>
After these settings, when you running a view in IE, it will be redirected to the FordidInfo page.
If your page does not use _layout, then you need to add the js on the corresponding view separately to ensure that it will not be displayed in IE.
Never do that. Excluding browsers with redirects, is what lead to massive user agent spoofing. Effectively making that AgentId useless. And the way you wrote it, you may get a lot of false-positives.
It is also a bad idea from the early days of the internet, we are hoping to get rid off:
https://en.wikipedia.org/wiki/User_agent#User_agent_spoofing

Is sending Session from javascript safe?

Well, this is a bit weird i think to ask this question, because i am not sure if that's the place to ask that.
OK, into the question..
I have this code
<script>
var session = "<%= Session["User"]%>";
</script>
So, i was thinking, is that safe? let me tell you what i mean..
I have a web api which you can get the name, last name, age and everything about the user with his Session, can i send this web api this session and use it?
Is that a safe thing to do ? in matter of securiy? if not, is there any better way?
EDIT 1:
What am i trying to aaccomplish? simple, i will store the UserId in the session, the UserId will Guid, when the user is loogin in the javascript can send post to an API server to get info, the API will send the UserId from the session.
Is That ok?
Workflow that you describe looks fine. For me it seems safe to use some ID to get more information about some user, especially if this is supposed to be an API, at least, Facebook API uses such principle not being afraid of some hackers :)
My main concern here is the coding style when you try to mix code and view which is not good. If you really need to share some information between client and server sides then I would go with one of these options.
Option # 1 - Cookies
What is the difference between a Session and a Cookie?
You can keep some simple information in a cookie and get it this way :
Client : $.cookie('ID')
Server : Response.Cookies["ID"]
In this case there is no need to put in a mess your client side JS with C# code and cookies will be saved on users PC which means that nobody will see them except him.
Option # 2 - Templates
Server : put all needed information into hidden form or ViewState
Client : take information from hidden form using HTML selectors
Straight answer :
In general, if you worry only about safety then it is fine to use this code, it should not break security of your site.
Although, personally I do not like this approach because :
you will mix code and view, MVC was created to split them
it is not clear where exactly in your view you will put this code and thus it is not clear how you are going to check that this variable was initialized
it may happen that you will put there some value that will break JS syntax and will cause JS error
In my personal opinion, I would replace it with one of the mentioned options.
Option 1 - MVC + JQuery + Cookie Example
public ActionResult Index()
{
string demo = Request.QueryString["MyNameSpace.ID"]; // get value from client
Response.Cookies["MyNameSpace.ID"].Value = "server"; // change value in response
return View();
}
Then in your JS file :
$(document).ready(function() { // make sure server rendered page
var ID = $.cookie('MyNameSpace.ID'); // get cookie value from server
$.cookie('MyNameSpace.ID', 'client'); // update, on the next request server will get it
});
Option 2 - MVC + JQuery + Templates Example
public class OptionsModel // View Model
{
public string ID { get; set; }
public string User { get; set; }
}
public ActionResult Index() // Controller
{
OptionsModel options = new OptionsModel();
options.ID = "server";
return View(options);
}
Your view :
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<OptionsModel>" %>
<%=Html.HiddenFor(m => Model.ID, new { #class = "MyNameSpace:ID" })%>
<%=Html.HiddenFor(m => Model.User, new { #class = "MyNameSpace:User" })%>
Then in your JS file :
$(document).ready(function() { // make sure server rendered page
var options = $('[class^=MyNameSpace]') // get values from hidden fields
options[0] = 'client'; // update data
$.ajax({ data : options }); // create handler to send data back to server
});
Examples for Web Forms do not differ significantly.
The code you have posted will be rendered on the page as so when it hits the client (assuming you are using ASP.NET
<script>
var session = "John Smith";
</script>
This is due to the use of the server side scripting tags <%= %> (https://technet.microsoft.com/en-us/library/cc961121.aspx)
As a note its probably not the best thing in the world to fully expose the session to javascript if that is your intention. At the end of the day it depends what you are storing in there and using it for (but ASP.NET will also use it for certain things) but exposing it just opens another area for someone to attack.
http://www.owasp.org is a great place to learn more about securing your website.

MVC javascript redirect page not working

In MVC, the default views for a controller allow one to reach the edit page via selecting an item in an index and using that id to reach the specific edit page.
In this MVC edit page, I have a javascript that reacts to a change in a dropdown. The dropdown represents a subset of the potential id's available from the index page, and in general, someone will choose a different one than the currently displayed one.
The postback to the control works correctly in C#, and I can find the relevant model that goes with the id. It all appears correct on the C# controller side. However, when I try to get it to redirect back to the same edit page but with a different id (that from the dropdown), the page reverts back to the ajax call.
Is there anyway to "short-circuit" the ajax call so that it "knows" that it doesn't return but lets the C# redirect to the edit page (just like what happens when an element is chosen from the index page).
Thanks in advance,
Joseph Doggie
If you are making ajax requet, then you have to implement a way to redirect.
Depends on your ajax protocol... Are you returning json? html ...
If returning json, you could add a flag in your response telling wether this is a redirect answer and do redirect in js :
window.location = url
OK, there is at least one way to do this.
Assume editing X with Controller named YController:
JavaScript:
var MyControllerUrlSettings = {
MyControllerPrepareModifyXInfoUrl: '#Url.Action("PrepareModifyAssetInfo", "Y", new { x_txt = "param" })'
}
one then has a JavaScript to handle the dropdown change:
$('#ModelXList').change(function () {
//// alert('Change detected');
if ($("#ModelXList").val() != "") {
//// alert('Reached here');
var XNbrString = $("#ModelXList").val();
var trimmedXNbrString = $.trim(XNbrString);
//// debugger;
if (trimmedXNbrString != "") {
var url = MyControllerUrlSettings.MyControllerPrepareXInfoUrl;
window.location.href = url.replace('__param__', trimmedXNbrString);
}
}
else {
}
});
Finally, in the controller, there is a method:
public ActionResult PrepareModifyXInfo(string XNbr_txt)
{
// we cannot save anything here to cdll_cdcloanerlist;
// static variables must be used instead.
/// .... do what you have to do....
return RedirectToAction("ModifyEdit", new { XNbr_txt = XNbr_txt });
}
Note: For proprietary reasons, I changed some of the syntax so that everything would be general, therefore, you may have to work with the above code a little, but it works
Alternate answers are really welcome, also!

Simplest way to convert C# method to asynchronous call Page/Web Method or jQuery

I have a C# method from a web form that I want to call asynchronously with with either a Page Method, Web Method or possibly jQuery to avoid postback to the server. The method calls other methods and rebuilds a treeview and performs validation and sets some other values as well. Below is an example of the code.
What would be the simpleset way to do this?
Any code samples provided would be greatly appreciated if possible. No update Panels..
protected void btnSubmit_Click(object sender, ImageClickEventArgs e)
{
if (ValidateSelection() == true)
{
int ProductID = Convert.ToInt32(grdGetProducts.SelectedValue.ToString());
if (Convert.ToInt32(ddBetTypeID.SelectedItem.Value) != 2)
{
SubmitProduct(Convert.ToInt32(ddProductTypeID.SelectedItem.Value), currentWeek, Convert.ToInt32(ddProductID.SelectedItem.Value), Convert.ToInt32(ddValue.SelectedItem.Value), Convert.ToInt32(ddCost.SelectedItem.Value), ProductID);
}
if (Convert.ToInt32(ddProductTypeID.SelectedItem.Value) == 2)
{
int price;
price= 1;
//if 1 open side then part 2
if (txtProductID.Text != "0" && txt2ProductID.Text == "0")
{
price= 2;
}
if (ProductID > 2)
{
BuildTree(currentTime, Convert.ToInt32(ddProductID.SelectedItem.Value), currentProduct);
}
}
}
Without an update panel you are certainly going to need a lot of client side javascript.
I do things like this with dynamic tables all the time and the first thing I did was isolate my data and my display. You should start by doing the same.
Figure out what data is being validated and how you can gather it on the client to be sent to a web method. Then you need to write client scripts to do this gathering and posting. After you post with the client script, in the callback of the ajax call, you would then need to do the rendering on the page. Without an update panel this is all on you.
The issue you have without using an update panel is that you are going to have to recreate the treeview structure or manipulate it on the client side. This means replacing all the elements and their event handlers. This can be a very daunting task for the more complicated ASP controls.
What the server would need to send back for rendering would be all of the data the tree needs. It is on you to figure out what that is though.
I have dynamically rendered gridviews before because it is nice to use them to create complex styles. Then it is just a matter of creating rows with the script on the client, based on the data I get from the server; I imagine you can do something similar to this with a treeview.
I have learned a lot about jQuery, ajax and ASP.NET interactions by reading encosia.com. That individual is a wonderful resource.
Things to avoid using jQuery AJAX and ASP.NET
Mistakes: manual JSON serialization
Using jQuery to directly call asp.net page methods
Update Panels are Dangerous

How do I redirect to my parent action in MVC site?

I have been looking at several pages on here already such as:
How do I redirect to the previous action in ASP.NET MVC?
How can I redirect my action to the root of the web site?
Along with several hours of searching google.
No where seems to have an answer to my problem and I am sure it should be possible within MVC somehow hence the reason I am now here to ask the question.
So the problem I am facing is that I want to allow the user to change the language of the page by choosing a new language from a drop down menu which is in its own partial view hence the problem, I need to redirect to the parent action and not the child. This all works fine as long as i send the user back to the root of the site. Using the following code:
[HttpPost]
public ActionResult RegionSelect(RegionSelectionModel model)
{
var currentUser = Session.GetCurrentUser();
var currentDbUser = Session.GetUserEntity(_dataAccessLayer);
if (!ModelState.IsValid)
{
model.AvailableRegions = CacheHelpers.GetAvailableRegions<RegionView>(_dataAccessLayer, _cache).ToList();
return PartialView("_RegionSelect", model);
}
var selectedRegion = UsersControllerHelpers.SetSelectedRegion(model, _dataAccessLayer, _cache, _website.Client);
var uri = model.OriginalUrl;
var routeInfo = new RouteHelpers(uri, HttpContext.Request.ApplicationPath);
// Route Data
var routeData = routeInfo.RouteData;
routeData.Values.Remove("language");
var defaultClientLanguageCode = _website.Client.LanguagesSupported.FirstOrDefault().Code;
if (currentDbUser.Language.CountryCode != selectedRegion.PrimaryLanguage.CountryCode)
{
//TODO: Decide where to redirect or whether to refresh the whole page...
if ((defaultClientLanguageCode == selectedRegion.PrimaryLanguage.CountryCode) || (model.SelectedRegionId == 0))
{
UsersControllerHelpers.UpdateUsersRegions(currentUser, selectedRegion, _website.Client, _cache, _dataAccessLayer,
Session);
return RedirectToRoute(routeData.Values);
}
routeData.Values.Add("language",selectedRegion.PrimaryLanguage.CountryCode);
return RedirectToRoute(routeData.Values);
}
return RedirectToRoute(routeData.Values);
}
Two of my return statements return to the root page and one returns to the root but with a language so it would be "http://mysite/en-En/" but what if the user is on a page other than the root site? I want to somehow redirect them back to this same action but with the correct language string at the start.
How can i do this?
I have thought of several "hacky" ways of doing this, such as splitting the URL and swapping the language codes over. But ideally I am looking to do this as clean as possible.
Can anyone give me any idea's? Or is it just not possible?
It seems like it should be really simple but apparently not.
Thanks in advance for any help that you can provide.
EDITED
Added new code that is using code from suggested answer below.
I am now having two new problems.
I am getting this error message, if there are any things in the URL such as ?page=1:
A potentially dangerous Request.Path value was detected from the client (?)
If i try and remove the language completely using .Remove(). It removes it fine but when i try and redirect to the page in the default language it adds language?=language to the end of the URI.
Any ideas how i can resolve these two issues?
This option is definitely my answer. Leave me a comment if you need me to drop some code, and I can do that, but the examples on the linked website should get you started.
Use this method to change Request.UrlReferrer into Route data, then merge your language into that, then do a RedirectToRoute with the modified Route data.
Just use RouteData.Values.Add, RouteData.Values.Remove, and RouteData.values["whatever"], then pass that modified RouteData.Values object to RedirectToRoute()

Categories