ASP.NET parameter/variable in both app Settings and app Build Events - asp.net

In ASP.NET, we have Application Settings, and we have Application Pre/Post Build Events. Is it possible to access a Setting from the Pre Build Event? Or is it possible to inject the value of a Setting from the Pre Build Event?
Full context:
I have an Angular app embedded in an ASP.NET 4 Web API app. That is, my app is structured like this:
+ Solution
| - Project
| + Properties
| + AngularApp
| | + dist
| | + e2e
| | + src
| | - (etc)
| + App_Start
| + Model
| + Global.asax
| - Web.config
- ProjectTest
I have some URL rewrite rules so that any request website.com/x that doesn't refer to one of my Controllers will instead be redirected to website.com/AngularApp/dist/AngularApp/x. Everything I've described so far is working great.
The trouble is, this app is not being deployed to the root of the domain; it's being deployed to a subdirectory of the domain. (i.e. website.com/app instead of website.com). Three different portions of my app need to know that this new subdirectory should be considered root - the Angular app needs to have this configured as root, the rewrite rules need to incorporate this, and a certain pieces of C# code also needs to know about this new root (I'll save you the details here). Currently, I've had to specify this subdirectory in both the Pre Build Events (which are building the Angular app) and the Web.config (which controls my rewrite rules) and the Settings (which are accessed by my aforementioned C# code). It'd be better if I could have a single configuration that they all pulled from. My above question would at least allow the combination of two of these disparate three configurations.

My suggestion is not to do with any build events, it's rather to bootstrap your angular app with some values from hosting ASP view.
If I understand your app layout correctly, you might be able to push some variables onto a Razor view as plain javascript that would get picked up by Angular upon startup.
Model
public class SampleViewModel
{
public string Path { get; set; }
}
Controller
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View(new SampleViewModel() { Path = "/app/1/test" });
}
}
View
#model HelloWorldMvcApp.SampleViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>
</head>
<body>
<h1>Server-side path #Model.Path</h1>
<hr/>
<div ng-app="myApp">
<div ng-controller="MyController">
<h1>{{message}}</h1>
</div>
</div>
<script type="text/javascript">
var clientSidePath = "#Model.Path"; #* Razor happily renders values into javascript blocks *#
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.message = 'Client-side path ' + clientSidePath;
});
</script>
</body>
</html>
The absolute minimum Dotnet fiddle here: https://dotnetfiddle.net/pO2wHN. For simplicity sake I opted for AngularJS (which is probably not the exact version you use) but similar approach should work with recent Angular as well.

Related

Oracle Jet routing is adding extra folder in path when trying to get child module files

Problem
I am trying to write a Single Page Application (SPA) where initially the app shows module "A". When the user clicks an element in "A", module "B" is displayed and is passed an ID from A. (For example A displays a list of Employee IDs, clicking on one employee means B will display details of that employee)
Initially my URL is :
http://localhost:8000/
Clicking on an item in A with an id of 123, the URL changes to following which is correct:
http://localhost:8000/A/123
However, I get the following error
GET http://localhost:8000/b/js/viewModels/B.js net::ERR_ABORTED 404 (Not Found)
ojModule failed to load viewModels/B
ojlogger.js:257 Error: Script error for "viewModels/B"
I do not know why it has changed the path and added an extra "/b/" to get the B.js/B.html file. Of course it can not find this file as there is no such folder "b" in my project structure.
Oracle Jet Cookbook Sample
https://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=router&demo=stateParams
I am using the sample in the OracleJet Cookbook for a Router with State Parameters. If you open this example in full screen you see that the URL for the first screen (A) is
https://www.oracle.com/webfolder/technetwork/jet/content/router-stateParams/demo.html
Clicking on a person in the list changes the URL to the following, which is the same as mine.
https://www.oracle.com/webfolder/technetwork/jet/content/router-stateParams/demo.html/detail/7566
This cookbook sample does not error like mine.
My Code
project structure
src
|- index.html
|- js
|- main.js
|- viewModels
|- A.js
|- B.js
|- views
|- A.html
|- B.html
index.html
....
<body>
<div id="routing-container">
<div data-bind="ojModule:router.moduleConfig"></div>
</div>
</body>
....
main.js
requirejs.config(
{
baseUrl: 'js',
....
}
require(['ojs/ojbootstrap', 'ojs/ojrouter', 'knockout', 'ojs/ojmodule-element-utils', 'ojs/ojknockout', 'ojs/ojmodule'],
function (Bootstrap, Router, ko) {
// Retrieve the router static instance and configure the states
var router = Router.rootInstance;
router.configure({
'a': {label: 'a', value: 'A', isDefault: true},
'b/{id}': {label: 'b', value: 'B' }
});
var viewModel = {
router: router
};
Bootstrap.whenDocumentReady().then(
function(){
ko.applyBindings(viewModel, document.getElementById('routing-container'));
Router.sync();
}
);
});
A.html
....
<div on-click="[[onClicked]]" >
....
</div>
...
A.js
define(['ojs/ojcore', 'ojs/ojrouter', 'knockout', '],
function (oj, Router, ko) {
function AViewModel(params) {
....
router = Router.rootInstance;
....
this.onClicked= function(event) {
router.go('b/'+ 123);
}
....
};
}
return AViewModel;
}
Attempts
I have tried adding one of the following in "main.js" and it doesn't make a difference.
Router.defaults['baseUrl'] = '';
Router.defaults['baseUrl'] = 'js';
Router.defaults['baseUrl'] = '/js';
Router.defaults['baseUrl'] = '/js/';
Updated answer after gleeming more information
I have finally resolved this with some advice from a colleague.
Make sure the "baseUrl" specified in your requireJS is absolute when you are using the urlPathAdapter router.
main.js
requirejs.config(
{
baseUrl: '/js',
RequireJS's baseUrl is used as the starting location for RequireJS to download its relative content. Most of the time its set to "js" but that will not work nicely with the UrlPathAdapter router. This is because the router will change the URL which RequireJS tries to add its path to when its not absolute. The result is that the path requireJS is trying to use to get its content is invalid.
For example:
you accessed your app with the URL "protocol://server:port/my/app"
RequireJS will try and access content by appending "js", for example "protocol://server:port/my/app/js/viewModel/..." which works when you are at the root of your application
you use the router to navigate to a different url, the url is now "protocol://server:port/my/app/newPath"
now RequireJS will try and use the URL "protocol://server:port/my/app/newPath/js/viewModel" which is wrong
When the RequireJS baseURL is absolute, it will always be added to the apps URL, for example "protocol://server:port/my/app/js/viewModel" where the content will be found
NOTE: I also ensured the baseUrl in "path-mapping" was absolute as well
path_mapping.json
{
"baseUrl": "/js",
Another solution was to change my router adapter from the default urlPathAdapter to urlParamAdapter.
Router.defaults.urlAdapter = new Router.urlParamAdapter();
var router = Router.rootInstance;

Json get not working through azure but working locally

I have created a asp.net webapi and hosted it through azure.
This works fine when I run host/api/carparks. It also works when I run an ODATA query string
host/api/carparks?$Filter%20eq%20%27Liverpool%27
Google chrome returns the results as JSON as I want them.
The problem I am having is, I need to create a "Client" application to visualize my data. I have created a really simple for loop to return my data for testing purposes, once I have data returned I can start creating my application.
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript">
function getStations() {
var town = document.getElementById("town").value;
var stationList = "<p>";
var uri = "http://localhost:38852/api/carparks?$filter=Town%20eq%20%27" + town + "%27";
$.getJSON(uri,
function (data) {
$('#here_data').empty(); // Clear existing text.
// Loop through the list of products.
$.each(data, function (key, val) {
stationList += val.Name + '<br />';
});
stationList += "</p>";
document.getElementById("here_data").innerHTML = stationList;
});
}
$(document).ready(getStations);
</script>
</head>
<body onload="getStations()">
<h1>Stations API</h1>
<p>Enter town</p>
<input type="text" id="town" value="Derby" />
<input type="button" value="Find Stations" onclick="getStations()" />
<div id="here_data">
<p>Car parks go here</p>
</div>
</body>
</html>
My client app works perfectly when I run my web api locally but when I change the getJSON request URI to my azure one (Which works in the browser!) nothing happens.
I have tried uploading my client app to azure and testing it that way but nothing :(
Is there any Azure settings that need to be changed?
Looks very much like a cross-origin issue.
The issue does not occur when you call the Service directly in your browser but only when you issue an Ajax call from a different domain (localhost vs. *.azurewebsites.net).
If you want to access your Web Api service with an Ajax call from a different domain you need to enable Cross Origin Resource Sharing (CORS).
A detailed article is found here:
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api
Quoted from the link:
Install-Package Microsoft.AspNet.WebApi.Cors
Open the file App_Start/WebApiConfig.cs. Add the following code to the
WebApiConfig.Register method.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Next, add the [EnableCors] attribute to the TestController class:
using System.Net.Http; using System.Web.Http; using
System.Web.Http.Cors;
namespace WebService.Controllers
{
[EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
}
For the origins parameter, use the URI where you deployed the
WebClient application. This allows cross-origin requests from
WebClient, while still disallowing all other cross-domain requests.
Later, I’ll describe the parameters for [EnableCors] in more detail.
Do not include a forward slash at the end of the origins URL.
Thanks to #viperguynaz and #florian I have fixed my issue. I changed the CORS option in Azure portal. (When I first did it I didn't remove the forward slash at the end of the URL). I removed the slash and it works.
I have also used the info given by #florian to help me understand CORS more.
Thanks again
1 happy joe :)

ASP.Net Boilerplate & jTable

I have studied the ASP.Net boilderplate template (http://www.aspnetboilerplate.com/Templates) and made some custom changes.
I have a "GetComponentDataList()" method in my services. I played around with that and rendered it as a list like shown here:
<!-- Component list -->
<ul class="list-group" ng-repeat="component in vm.components">
<div class="list-group-item">
<br />
<span>Entry:</span>
<span>{{component.entry}}</span>
</div>
</ul>
components.js code:
vm.refreshComponents = function () {
abp.ui.setBusy( //Set whole page busy until getComponentDataList complete
null,
componentDataService.getComponentDataList( //Call application service method directly from javascript
).success(function (data) {
vm.components = data.componentData;
})
);
};
Now I would like to render the components via jTable. jTable expects an action to get the list of data:
listAction: '/api/services/app/componentData/GetComponentDataList',
How do I use jTable from boilerplate template?
1. Do I need to add a method in my "HomeController" to use jTable?
2. The result of my "GetComponentDataList" method in my service is of type IOutputDto.
That means the result of my service is not directly a list. There is one indirection
level inbetween. Seems like this does not fit together.
3. Can I provide a function in my JS-ViewModel and use that function instead of an action URL?
Any hint would be awesome.
Thx.
I'm working on a sample project to work ABP with jTable. I did not finish it yet. But you can check it. https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/AbpWithjTable
Add abp.jtable.js to your file (https://github.com/aspnetboilerplate/aspnetboilerplate-samples/blob/master/AbpWithjTable/AbpjTable.Web/App/Main/libs/abp.jtable.js) after all ABP and jtable scripts.
See example js: https://github.com/aspnetboilerplate/aspnetboilerplate-samples/blob/master/AbpWithjTable/AbpjTable.Web/App/Main/views/people/people.js
See example C# App service: https://github.com/aspnetboilerplate/aspnetboilerplate-samples/blob/master/AbpWithjTable/AbpjTable.Application/People/PersonAppService.cs

Security for an AngularJs + ServiceStack App

I have an application that have four modules in the front end, I'm trying to use as much as possible AngularJs in the front end I'm using an empty website asp.net project to host all the files and the REST serviceStack, my project have kind of the following structure:
~/ (web.config, global.asax and all the out of the box structure for an asp.net website)
- App <- AngularJs
- Users <- js controllers and views (static html files)
- Companies
- BackEnd
- Public
Index.html
IndexCtrl.js
App.js
- Content
- Js
I use angularjs service calls and the backend I'm using REST with servicestack.
the question is how can I restrict the access only to authenticated users to those static html files? let's say the ones that are inside inside Companies, Backend and users for example
Hi After doing some research this is the solution that worked for me:
Install razor markdown from nuget
Change the file structure to match the default behavior RM [Razor Markdown] to /views
Modify the web config following the approach described in this service stack example
Change all the static htmls files to .cshtml files, this by default creates the same route without the extension like /views/{Pagename} without the extension, I'm just using this approach to get the authorization logic simpler to implement (at least for me)
Update the service method with an authorize attribute you can find out more in this page
to illustrate a lit of bit more this is my route definition in so far:
'use strict';
angular.module('myApp', ['myApp.directives', 'myApp.services']).config(
['$routeProvider', function($routeProvider) {
$routeProvider.when('/Dashboard', {
controller: 'dashboardCtrl',
templateUrl: 'Views/dashboard'
}).when('/Payments', {
controller: 'paymentsCtrl',
templateUrl: 'Views/payments'
}).
when('/Login', {
controller: 'loginCtrl',
templateUrl: 'Views/login'
});
}]
);
Notice that the references are pointed now to the razor paths.
this is a small menu I've done in angular
<div class="container">
<div class="navbar" ng-controller="indexCtrl">
<div class="navbar-inner">
<a class="brand" href="#/">header menu</a>
<ul class="nav">
<li ng-class="{active: routeIs('/Dashboard')}">Dashboard</li>
<li ng-class="{active: routeIs('/Login')}">Login</li>
<li ng-class="{active: routeIs('/Payments')}">payments</li>
</ul>
</div>
</div>
<ng-view></ng-view>
</div>
let's say that the payments page is restricted, so every time I click on a the page I get a 401 unauthorized message.
Service host:
public override void Configure(Container container)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
new FacebookAuthProvider(appSettings),
new TwitterAuthProvider(appSettings),
new BasicAuthProvider(appSettings),
new GoogleOpenIdOAuthProvider(appSettings),
new CredentialsAuthProvider()
})); //I'm going to support social auth as well.
Plugins.Add(new RegistrationFeature());
Routes.Add<UserRequest>("/Api/User/{Id}");
Routes.Add<LoginRequest>("/Api/User/login","POST");
Routes.Add<PaymentRequest>("/views/Payments");
}
I hope that helps
Create a CatchAllHander method to check for restricted routes and, for those static files that require authentication, return the ForbiddenFileHander if not authenticated, otherwise return null. Given an isAuthenticated method and restrictedDirs is defined somewhere - maybe your app or web config file, it can be as simple as:
appHost.CatchAllHandlers.Add((httpMethod, pathInfo, filePath) => {
if ( restrictedDirs.ContainsKey(pathInfo) && !isAuthenticated())
return new ForbiddenHttpHandler();
return null;
});
Why not use Forms Authentication? Simply add a few < location > tags to your web.config to allow/disallow different sections, you can even do it based on roles.

Excluding bootstrap from specific routes in Meteor

I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)

Resources