MVC PagedList sending weird requests to server - c#

I am using MVC with PagedList in order to have a big table divided into multiple pages.
Now, in the web browser, this is what I see:
http://localhost:49370/Home/Pending?page=2
Which makes sense. However, when sending a request to the server, this is what the server receives: http://localhost:49370/Home/WhereAmI?_=1429091783507
This is a huge mess, and in turn it makes it impossible to redirect the user to specific pages in the list because I don't know what is the page the user is currently viewing !
Controller code:
public ActionResult Pending(int? page)
{
//I have a ViewModel, which is MaterialRequestModel
IEnumerable<MaterialRequestModel> model = DB.GATE_MaterialRequest
.Select(req => new MaterialRequestModel(req))
.ToList();
int pageNum = page ?? 1;
return View(model.ToPagedList(pageNum, ENTRIES_PER_PAGE));
}
View code:
#model IEnumerable<MaterialRequestModel>
<table>
//table stfuff
</table>
<div style="display: block;text-align: center">
#Html.PagedListPager((PagedList.IPagedList<MaterialRequestModel>)Model, page => Url.Action("Pending", new { page }), PagedListRenderOptions.ClassicPlusFirstAndLast)
</div>
Is this a limitation of MVC PagedList? Or am I missing something?

It happens that PagedList does not send this type of information to the server. Instead, if you want to know which page is being looked at, you have to use a custom model that has that information, and if you want to make a request usign ajax (the original objective here) you must using a special option:
#Html.PagedListPager(Model.requests, page => Url.Action("SearchTable", "Home",
new
{
employeesQuery = Model.query.employeesQuery, //your query here
pageNum = page
}
), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "tableAndPaginationDiv", OnComplete = "initiatePendingTableDisplay" }))
Admittedly this solution is poor at best. You can only have 1 option of the entire list (so if you are already using other options somewhere else you can forget it) and you have no control whatsoever on the request made, so customization is really not an option here unless you feel like hooking the calls.
Anyway, this is how I fixed it, hopefully it will help someone in the future!

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

How to prevent people from keep looping HTTP POST to a function?

I am trying to develop a website, the website got a pop-up modal which allows the user to subscribe to our latest promotion. In that input, we got a textbox to allow users to key in their email.
However, when we look at the HTML code, the HTTP POST URL is visible:
If someone is trying to use this URL, and spam HTTP POST requests (see below), unlimited entries can be created in the subscriber database table.
for (int a = 0; a < 999999; a++)
{
var values = new Dictionary<string, string>
{
{ "email", a+"#gmail.com" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://www.example.com/recepticle.aspx", content);
var responseString = await response.Content.ReadAsStringAsync();
}
How can I prevent this from happening? We cannot put a capcha, since this is subscriber to our promotion.
Edit: Please note that a ANTI-forgery token will not work, because the hacker can download entire HTML string using GET, and get the value from the anti forgery token textbox and POST the value to the POST URL again, so it will not work and the same anti-forgery token can use multiple times, it is not secure.
You can choose one of the below option to implement what you are looking for.
1- Implement CAPTCHA/re-CAPTCHA, it will make sure that using any tool request can't be made. I understand that you don't want to use CAPTCHA, I still feel you should go with it, as it is the best approach to handle this type of scenarios.
2- IP Based restriction, lock submitting the request from one IP for some time.
3- Other option can be OTP (one time password), you can send the OTP to the email, and only after successful verification you can register the email.
Use AntiForgeryToken. Read more about Antiforgery Tokens here
In your form Razor View, Add an #Html.AntiForgeryToken() as a form field.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#*Rest of the form*#
}
In your Action Method use ValidateAntiForgeryTokenAttribute
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit( MyViewModel form)
{
if (ModelState.IsValid)
{
// Rest of ur code
}
}

How can I pass two parameters from ActionLink to ActionResult

I'm implementing a website using asp.net MVC. I'm trying assign role to user by clicking into the role. I've tried several ways, unfortunately, I failed.
Controller method
[HttpPost,ActionName("AssignUserToRole")]
[ValidateAntiForgeryToken]
[CustomAuthorize(Roles ="SudoAdmin")]
public ActionResult AssignUserToRole(long userId, long roleId)
{
new BusinessUser().AddRoleToUser(userId, roleId);
return RedirectToAction("Index");
}
view
#Html.ActionLink(#r.RoleName, "AssignUserToRole", "Users", new { userId = Model.UserId, roleId = r.RoleId })
first I tried Jquery Ajax, but it failed, even though I think it was not a very great idea. However, I still would like to do it with "Ajax"I mean without refreshing the page.
I hope my question is good enough
thanks for all of you
First, the action is set to respond only to POST. That means unless you request it via POST, you'll get a 404, since there's no action that can respond to any other request method.
As a link, it will always make the request via GET. If you employ JavaScript, you can bind to the click event and send the request via POST using AJAX, but again, you have to send it as POST. If you use something like $.get or $.ajax with the method as GET, it won't work.
Ideally, if you intend to hit the action via POST, then you should employ a form element in your view. Using CSS, you can style the submit button to look like a link, if that's what you want, but the underlying structure should be a form.

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!

Categories