I'm having some problems publishing an asp.net mvc3 application. When deployed, the application fails with "The controller for path '/Dashboard/Alarmes' was not found or does not implement IController" where Alarmes is an action at DashboardController. Not sure if it has something to do with it, but Alarmes return an Json result.
Another thing I noticed is that some assemblies, that are referenced by another project in the same solution, are not deployed (only if I reference them in the mvc project itself).
Any tips on these?
Update:
The routes registration:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*allpng}", new { allpng = #".*\.png(/.*)?" });
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
routes.IgnoreRoute("{directory}/{resource}.asmx/{*pathInfo}");
routes.MapRoute("Default", "{controller}/{action}/{id}", new {
controller = "Dashboard",
action = "Index",
id = UrlParameter.Optional
});
}
the action:
public ActionResult Alarmes() {
var alarmesPorPonto = new Dictionary<string, List<Ponto>>();
var alarmes = _repositorioDeAlarmes.Pesquise(ObtenhaInicio(), DateTime.Today);
foreach (var alarme in alarmes) {
var tipo = alarme.Tipo;
var ponto = alarme.Ponto;
if (!alarmesPorPonto.ContainsKey(tipo.Nome)) {
alarmesPorPonto.Add(tipo.Nome, new List<Ponto>());
}
if (!alarmesPorPonto[tipo.Nome].Contains(ponto)) {
alarmesPorPonto[tipo.Nome].Add(ponto);
}
}
return Json(alarmesPorPonto.Select(a => new { Tipo = a.Key, a.Value.Count }), JsonRequestBehavior.AllowGet);
}
Another missing info: I'm deploying this application to a virtual directory.
Update 2
the full controller class:
public class DashboardController : Controller {
private readonly IRepositorioDeAlarmes _repositorioDeAlarmes;
private readonly bool _enableMap;
public DashboardController(IRepositorioDeAlarmes repositorioDeAlarmes) {
_repositorioDeAlarmes = repositorioDeAlarmes;
_enableMap = Convert.ToBoolean(ConfigurationManager.AppSettings["EnableMap"]);
}
public ActionResult Index() {
ViewBag.EnableMap = _enableMap;
return View();
}
public ActionResult Alarmes() {
var alarmesPorPonto = new Dictionary<string, List<Ponto>>();
var alarmes = _repositorioDeAlarmes.Pesquise(ObtenhaInicio(), DateTime.Today);
foreach (var alarme in alarmes) {
var tipo = alarme.Tipo;
var ponto = alarme.Ponto;
if (!alarmesPorPonto.ContainsKey(tipo.Nome)) {
alarmesPorPonto.Add(tipo.Nome, new List<Ponto>());
}
if (!alarmesPorPonto[tipo.Nome].Contains(ponto)) {
alarmesPorPonto[tipo.Nome].Add(ponto);
}
}
return Json(alarmesPorPonto.Select(a => new { Tipo = a.Key, a.Value.Count }), JsonRequestBehavior.AllowGet);
}
}
I suspect that you have hardcoded the url in your javascript when invoking the action instead of using an url helper.
So you wrote:
<script type="text/javascript">
$.getJSON('/Dashboard/Alarmes', function(result) {
...
});
</script>
instead of:
<script type="text/javascript">
$.getJSON('#Url.Action("Alarmes", "Dashboard")', function(result) {
...
});
</script>
which would have generated the correct url in the case when your application is hosted in a virtual directory which would be:
<script type="text/javascript">
$.getJSON('/MyAppName/Dashboard/Alarmes', function(result) {
...
});
</script>
What if you try to clean the ASP.Net temporary files and restart the web server?
[{windows-path}\Microsoft.NET\Framework{framework-version}\Temporary ASP.NET Files]
Related
I have an ASP.Net Core 3.1 app with the following startup.cs (I have tried various combinations of the below configuration based on web searches):
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<RequestLocalizationOptions>(options =>
{
options.RequestCultureProviders = new[] { new CookieRequestCultureProvider() };
});
and
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
And in my app's logon method, I'm setting the Culture Cookie as follows:
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture([logged-in-user].CultureCode)));
In subsequent requests I can see the cookie in my browser dev tools Network tab:
Yet, on the server, any given request still maintains the default server culture (which is en-ZA) for my dev environment. (I'm seeing this by checking System.Threading.Thread.CurrentThread.CurrentCulture.Name in any breakpoint in my server action methods)
And I'm running into date conversion issues between my client and my server (e,g client with en-US culture as per screenshot above) sends a date of 3/5/2009 (March 5th) to the server, and the server is interpreting it as May 3rd.
Why is my server not honoring the CultureCookie? What am I missing?
As you mentioned, you have registered your localization service in your ConfigureServices method.
My suggested way is to use it like:
services.AddLocalization(options => options.ResourcesPath = "Resources");
services
.AddControllersWithViews()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
But don't forget to register the middleware:
// In StartUp.cs Configure method
var SupportedCultures = new CultureInfo[]
{
new CultureInfo("en"),
new CultureInfo("zh")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(defaultLanguage),
SupportedCultures = SupportedCultures,
SupportedUICultures = SupportedCultures
});
As for your cookie end-time issue, please try to specify the end date of your cookie. Like this:
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions
{
Expires = DateTimeOffset.UtcNow.AddYears(1),
SameSite = SameSiteMode.None
});
I'm on asp.net core 6.0.
Thanks Anduin for the response, that helped me a lot! The official doc was missleading.
I found the key points for me were:
Service configuration:
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("zh-CN")
};
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
App support:
var SupportedCultures = new[]
{
new CultureInfo("en"),
new CultureInfo("zh")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(culture: "en", uiCulture: "en"),
SupportedCultures = SupportedCultures,
SupportedUICultures = SupportedCultures
});
Resource files with lang name, but not ISO code:
.\Resources\Controllers\HomeController.zh.resx
.\Resources\Views\Home\Index.zh.resx
Change culture programmatically using Ajax(I added an empty default):
#using Microsoft.AspNetCore.Builder
#using Microsoft.AspNetCore.Http.Features
#using Microsoft.AspNetCore.Localization
#using Microsoft.AspNetCore.Mvc.Localization
#using Microsoft.Extensions.Options
#inject IViewLocalizer Localizer
#inject IOptions<RequestLocalizationOptions> LocOptions
#{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var cultureItems = new List<SelectListItem>();
cultureItems.Add(new SelectListItem { Value = "", Text = Localizer["Select Lang"].Value });
cultureItems.AddRange(LocOptions.Value.SupportedUICultures
.Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
.ToList());
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}";
}
<div title="#Localizer["Request culture provider:"] #requestCulture?.Provider?.GetType().Name">
<form id="selectLanguage" asp-controller="Home"
asp-action="SetLanguage" asp-route-returnUrl="#returnUrl"
method="post" class="form-horizontal" role="form">
<label asp-for="#requestCulture.RequestCulture.UICulture.Name">#Localizer["Language:"]</label>
<select name="culture" onchange="this.form.submit();" asp-for="#requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems">
</select>
</form>
</div>
[HttpPost]
public IActionResult SetLanguage(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return LocalRedirect(returnUrl);
}
The IStringLocalizer (I'm keeping the _logger, which is removable):
private readonly ILogger<HomeController> _logger;
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(ILogger<HomeController> logger, IStringLocalizer<HomeController> localizer)
{
_localizer = localizer;
_logger = logger;
}
public IActionResult Index()
{
ViewData["Message"] = _localizer["Test Text"];
return View();
}
IViewLocalizer in index.cshtml:
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer Localizer
#{
ViewData["Title"] = Localizer["Title Text"];
}
And define the keys Test Text and Title Text in resource files.
In my mvc app I have combobox, I am trying to do the following: when the user choose some item that will call to some function from the controller.
Index.html:
#using (Ajax.BeginForm("dor", "Home", new AjaxOptions
{
HttpMethod = "Get",
//UpdateTargetId = "myPic",
InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace
}))
{
#Html.DropDownListFor(x => x.SelectedFileName, Model.Files, new { Name = "map", #class = "form-control", onchange = "CallChangefunc()" })
}
.
.
.
<script type="text/javascript">
function CallChangefunc() {
window.location.href = '#Url.Action("dor","Home")';
}
</script>
HomeVM:
public class HomeVM
{
public List<SelectListItem> Files { get; set; }
public string SelectedFileName { get; internal set; }
public List<string> DynamicAlgorithems { get; set; }
}
Homecontroller:
public void dor()
{
//some code
}
The problem: when the user choose some item from combobox it redirect me to blank page i.e to http://localhost:55354/Home/dor, but I only want to call the function named dor, not to go to blank page!.
what am I missing?
Related
window.location.href always redirect to a new page URL assigned to it. You should use jQuery.ajax() to call the action method when onchange method triggered and return desired result:
jQuery (inside $(document).ready())
$('#SelectedFileName').change(function() {
var selectedFileName = $(this).val();
$.ajax({
type: 'GET',
url: '#Url.Action("Dor", "Home")',
data: { fileName: selectedFileName },
success: function (result) {
// do something
}
});
});
Controller
[HttpGet]
public ActionResult Dor(string fileName)
{
// do something
}
Note: Make sure that action method argument has same name with assigned data passed from AJAX callback.
I've used Umbraco 7.3 and ASP.NET MVC 5 in my project.
I want to Send data from AngularJS to ASP.NET MVC 5 controller.
How can I do it?
reply.html :
<div ng-controller="Reply.controller">
<input type="button" name="Send Reply" ng-click="SendReply()"/>
</div>
Reply.controller.js:
angular.module("umbraco")
.controller("Reply.controller", function ($scope) {
$scope.SendReply = function () {
var SendTo = $("#Email").val();
var TextMessage = $("#TextMessage").val();
//TODO: It's need to write some codes to handle data to an action in ASP.NET MVC controller.But how?
}
});
ASP.NET MVC controller:
public class IncomingCallSurfaceController : BaseSurfaceController
{
public ActionResult Reply(SendMailModel sendMailModel)
{
//TODO: how I should be write this method that be proper for getting data from angularjs?
return null;
}
}
SendMailModel:
public class SendMailModel
{
public string TextMessage { get; set; }
public string SendTo { get; set; }
}
package.manifest:
{
propertyEditors: [
{
alias: "Send.Reply",
name: "Send Reply",
editor:{
view:"/App_Plugins/Reply/Reply.html"
},
}
]
,
javascript:[
'/App_Plugins/Reply/Reply.controller.js'
]
}
Updated: add a picture of structure of solution folders
Reply.controller.js :
angular.module("umbraco")
.controller("Reply.controller", function ($scope, $http, $routeParams) {
$scope.SendReply = function () {
var sendTo = $("#Email").val();
var textMessage = $("#TextMessage").val();
var contentId = $routeParams.id;
$scope.xxx = "I'm here!";
var dataObj = {
TextMessage: textMessage,
SendTo: sendTo,
ContentId: contentId
};
$http.post("backoffice/Reply/ReplyToIncomingCall/ReplyMessage", dataObj)
.then(function (response) {
alert("YES!");
//TODO:
});
}
});
ReplyToIncomingCallController.cs :
namespace Jahan.Nuts.Web.Mvc.UmbracoCms.App.App_Plugins.Reply
{
[PluginController("Reply")]
public class ReplyToIncomingCallController :UmbracoAuthorizedJsonController
{
[HttpPost][ChildActionOnly]
public ActionResult ReplyMessage(SendMailViewModel vm)
{
return null;
}
}
}
SendMailViewModel :
public class SendMailViewModel
{
public string TextMessage { get; set; }
public string SendTo { get; set; }
public int ContentId { get; set; }
}
Tree Structure Of Files :
If you want to know more about backoffice-routing in Umbraco 7.x, you can visit this link.
Firstly, when you use Angular try to avoid use jQuery.
Use ng-model from angular to bind input value to your controller. And use $http module to send data to API.
View:
<div ng-controller="Reply.controller">
<input type="text" ng-model="message.TextMessage"/>
<input type="text" ng-model="message.SendTo"/>
<input type="button" name="Send Reply" ng-click="SendReply()"/>
</div>
Angular:
angular.module("umbraco")
.controller("Reply.controller", function($scope, $http) {
$scope.message = {
TextMessage: '',
SendTo: ''
};
$scope.SendReply = function() {
//TODO URL
$http.post('/umbraco/api/IncomingCallSurface/Reply', $scope.message)
.then(function(response) {
//TODO
});
}
});
ASP.NET MVC:
public class IncomingCallSurfaceController : UmbracoApiController
{
[HttpPost]
public ActionResult Reply(SendMailModel sendMailModel)
{
//TODO: how I should be write this method that be proper for getting data from angularjs?
return null;
}
}
what else do i need in my code please, I have this so far:
<script type="text/javascript">
function PostNewsComment(newsId) {
$.ajax({
url: "<%= Url.Action("AddCommentOnNews", "Home", new { area = "News" }) %>?newsId=" + newsId + "&newsComment=" + $("#textareaforreply").val(), success: function (data) {
$("#news-comment-content").html(data + $("#news-comment-content").html());
type: 'POST'
}
});
}
$("#textareaforreply").val("");
</script>
and
[HttpPost]
[NoCache]
public ActionResult AddCommentOnNews(int newsId, string newsComment)
{
if (!String.IsNullOrWhiteSpace(newsComment))
{
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
ZincService.NewsService.AddCommentOnNews(newsId, newsComment, currentUser.UserId);
Zinc.DataModels.News.NewsCommentsDataModel model = new DataModels.News.NewsCommentsDataModel();
var today = DateTime.UtcNow;
model.CommentDateAndTime = today;
model.NewsComment = newsComment;
model.Firstname = currentUser.Firstname;
model.Surname = currentUser.Surname;
model.UserId = CurrentUser.UserId;
return View("NewsComment", model);
}
return null;
}
<div class="actions-right">
<%: Html.Resource(Resources.Global.Button.Reply) %>
</div>
i have no idea how this works, because it is not working in FF???
and the other thing is i must not pass return null i must pass JSON false ???
any help please?
thanks
You should encode your request parameters. Right now you have concatenated them to the request with a strong concatenation which is a wrong approach. There's a property called data that allows you to pass parameters to an AJAX request and leave the proper url encoding to the framework:
function PostNewsComment(newsId) {
$.ajax({
url: '<%= Url.Action("AddCommentOnNews", "Home", new { area = "News" }) %>',
type: 'POST',
data: {
newsId: newsId,
newsComment: $('#textareaforreply').val()
},
success: function (data) {
$('#news-comment-content').html(data + $('#news-comment-content').html());
}
});
}
Also you haven't shown where and how you are calling this PostNewsComment function but if this happens on the click of a link or submit button make sure that you have canceled the default action by returning false, just like that:
$('#someLink').click(function() {
PostNewsComment('123');
return false;
});
and the other thing is i must not pass return null i must pass JSON false ???
You could have your controller action return a JsonResult in this case:
return Json(new { success = false });
and then inside your success callback you could test for this condition:
success: function (data) {
if (!data.success) {
// the server returned a Json result indicating a failure
alert('Oops something bad happened on the server');
} else {
// the server returned the view => we can go ahead and update our DOM
$('#news-comment-content').html(data + $('#news-comment-content').html());
}
}
Another thing you should probably be aware of is the presence of dangerous characters such as < or > in the comment text. To allow those characters I would recommend you build a view model and decorate the corresponding property with the [AllowHtml] attribute:
public class NewsViewModel
{
public int NewsId { get; set; }
[AllowHtml]
[Required]
public string NewsComment { get; set; }
}
Now your controller action will obviously take the view model as argument:
[HttpPost]
[NoCache]
public ActionResult AddCommentOnNews(NewsViewModel viewModel)
{
if (!ModelState.IsValid)
{
var currentUser = ZincService.GetUserForId(CurrentUser.UserId);
ZincService.NewsService.AddCommentOnNews(viewModel.NewsId, viewModel.NewsComment, currentUser.UserId);
var model = new DataModels.News.NewsCommentsDataModel();
var today = DateTime.UtcNow;
model.CommentDateAndTime = today;
model.NewsComment = newsComment;
model.Firstname = currentUser.Firstname;
model.Surname = currentUser.Surname;
model.UserId = CurrentUser.UserId;
return View("NewsComment", model);
}
return Json(new { success = false });
}
In my other question I found out that there is no whitelist for windows phones.
Now I am looking for a native code workaround but I have never written a line of native code for windows phones. So it's not easy for me. I think I can download a page like this:
void GetAirportData()
{
var url = new Uri("http://server.example.com/data.php", UriKind.Absolute);
var webClient = new WebClient();
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
webClient.OpenReadAsync(url, url);
}
But how can a get this data to my javascript app?
Here is a workaround. The following code is a Phonegap command that implements Cross Domain Call functionality.
using System;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using WP7CordovaClassLib.Cordova;
using WP7CordovaClassLib.Cordova.Commands;
using WP7CordovaClassLib.Cordova.JSON;
namespace Cordova.Extension.Commands //namespace is predefined, don't change it!
{
public class Cdc : BaseCommand //Cross domain call
{
[DataContract]
public class CdcOptions
{
[DataMember(Name = "path")]
public string Path { get; set; }
}
public void Call(string args)
{
CdcOptions options = JsonHelper.Deserialize<CdcOptions>(args);
var url = new Uri(options.Path, UriKind.Absolute);
var webClient = new WebClient();
webClient.OpenReadCompleted += (s, e) =>
{
if (e.Error != null)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error"));
return;
}
//Stream -> string
var sr = new StreamReader(e.Result);
var result = sr.ReadToEnd();
DispatchCommandResult(
new PluginResult(PluginResult.Status.OK, result));
};
webClient.OpenReadAsync(url, url);
}
}
}
Test on the client side:
<script type="text/javascript">
function cdc(path, success, fail) {
PhoneGap.exec(
success, //success
fail, //fail
"Cdc", //service
"Call", //action
path //args
);
};
function onDeviceReady(e) {
cdc(
{
path: "http://stackoverflow.com/questions/9291809/workaround-for-missing-whitelist-in-phonegap-for-windows-phone"
},
function (arg) {
document.getElementById('test').innerHTML = arg;
}, function (arg) {
document.getElementById('test').innerHTML = arg;
});
}
document.addEventListener("deviceready", onDeviceReady, false);
</script>
</head>
<body>
<div id="test"></div>
</body>
</html>