I'm very new to web development but I wanted to create a web api for an existing Windows Service application I created for work.
My goal right now is to create a simple web app consisting of some text input fields where someone can enter their email and subscribe to a mailing list.
I was pointed in the direction of using OWIN to self-host a web api in my existing project so I looked at this guide to get started:
https://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api
This guide shows the console app calling and displaying the API, but I wanted to do the same from a web app. So I tried to do what is accomplished here in the 'Getting Started with ASP.NET Web Api 2' guide using a console application with OWIN.
When I start my console application, I can enter http://localhost:8080/api/values/1 and I get <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">value</string> Which I assume means the web server is running and responds to the request to get my string value that I'm returning from my ValuesController.
However I've been trying to create a simple web page that calls the API to get the value but I haven't been able to figure it out.
Right now my index.html has the following in the body:
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
<script>
var uri = "http://localhost:8080/api/values/1";
$(document).ready(function () {
$.getJSON(uri)
.done(function (data) {
$('#value').text("done");
})
.fail(function (jqXHR, textStatus, err) {
$('#value').text('Error: ' + err);
});
});
</script>
And I just get Error: printed on the page. Again I'm new to working on web related technologies so any guidance would be much appreciated.
Ok so, following the discussion in comments (helping you to debug the app).
The cross-origin security policy in browsers restricts web pages from calling an data source that exists on a different domain. A domain is either a different url, or a different port at the same url.
http://localhost:80
is a different url/domain from:
http://localhost:81
There are two solutions to this, either utilise JSONP, or CORS to make the request to the api, JSONP basically wraps your json data up in a function call. When the api call returns, it executes the callback/function call, bypassing the security restriction.
However, both JSONP and CORS require server support, which brings solution 2...
Serve up the webpage you are trying to use from the API, meaning you server it from the same domain, hence no cross-domain issues!!
Glad you have got it sorted, I know it can be frustrating with web calls.
Related
I have an asp.net core 3.0 application hosted behind a proxy server. The URL is rewritten when forwarded to the docker image that is running the application. I can't seem to get authentication to work as the redirect is computed to the wrong url. When i manually fix the redirect url, the authorization end point doesn't catch the call back. The documentation seems to indicate some combination of using UsePathBase and/or setting the Request.PathBase. I've tried this and still can't seem to get it correct. Here is my setup.
public url: http://app.com/foo/api/1
docker url: http://docker/
When the OAuth Authentication (Microsoft.AspNetCore.Authentication.OAuth) computes the redirect, it computes http://app.com/authorization-code/callback which is incorrect. I've tried manually setting the callback path to get around that issue. However, now the /authorization-code/callback endpoint is not hit when the authentication server redirects back to http://app.com/foo/api/1/authorization-code/callback
What are the correct settings in this situation?
thanks for the help
~ Paul
I am invoking API calls to get JSON data from server using javascript.
$(function () {
var customersTable = $('#Customers');
var returnCustomersTable = UpdateDataTable(customersTable, "/Customers/Loaddata");
}
This works fine when I am working with Visual studio Dev environment , because my web application is on root and so all javascripts Works fine.
Example:
My WebSite URL: http://localhost:4391
API Calls will be: http://localhost:4391/Customers/Loaddata
This works fine.
But when I deploy application to IIS, my website URL will be,
My WebSite URL: http://localhost/MyAppName
But API calls would still be,
API Calls will be: http://localhost/Customers/Loaddata, which results in not found.
Since I am using javascript in a separate file , I wont be able to do URL.Action.
The other option would be to create a base URL for dev and prod and the append with each servcie call.
But I am thinking if there is any other way.
The routing configuration in ASP.NET MVC is designed to serve as the single place in the application where all of the URLs can be maintained.
It helps considerably with maintenance of the project if all URLs are generated using routing through one of the UrlHelper based methods, such as Url.Action() rather than hard coded in controllers and views.
As long as your JavaScript is in an MVC view, you can just resolve Url.Action inline.
$(function () {
var customersTable = $('#Customers');
var returnCustomersTable = UpdateDataTable(customersTable,
'#Url.Action("Loaddata", "Customers")');
}
If the JavaScript is in a separate file outside of MVC, consider passing the URL through a variable or function parameter.
This routing feature also makes it easier to deploy ASP.NET MVC, because the generated URLs will adapt to use the application path when the application is not deployed to the root website virtual directory.
Application running in IIS web site root:
/Customers/Loaddata
Application running in virtual subdirectory configured as IIS Application named "MyAppName":
/MyAppName/Customers/Loaddata
History: I have a tiny app that has lived on a linux web server for a while: html5/javascript/perl cgi scripts. There is a sort of third party middle ware called Siteminder from CA that provides SSO services and it works fine. In my case on the linux box there is a dir in the DOCROOT that holds the Public facing html, js & perl cgi scripts. There is a different dir where the pages and scriots for the authorized content sits. Siteminder is configured to be aware of this auth-dir and the request paths that contain that auth-dir element.
Siteminder is tied into Apache and observes the request stream and when it sees a request with a path element that it cares about it holds the in-bound request; redirects the visitor to a branded auth page; deals with the auth flow and then, if authenticated, sends the original request on through. In this case the auth is tied to an AD group. Again, this works. My pages and code are totally unaware of the existence of Siteminder.
For reasons above my paygrade it has been decided to move the content from the linux box to an IIS server. Convert everything to C# .Net MVC. I am NOT a windows person but this is what is in my plate at the moment.
Our local Siteminder experts tell me that SM works exactly the same under IIS as linux. That once I convert my code that it doesn't need to be aware of SM either... yet something is not working.
In my case, due to user interaction a modal popup appears in the Public section (HomeController) that holds a small form. Clicking the submit button triggers a jQuery GET (I've also tried PUT, POST and a redirect) action to a method in the AuthController, a la:
$.get({
'url': "/Auth/AddNewData",
'contentType': "application/x-www-form-urlencoded; charset=UTF-8",
'dataType': "json",
'traditional': true,
'data': {
'thing': myThing,
'otherThing': myOtherThing
}
}).done(function(data, textStatus, jqXhr) {
console.log("it worked");
}).fail(function(jqXhr, textStatus, errorThrown) {
console.dir(jqXhr);
console.log(textStatus);
console.log(errorThrown);
});
I am aware that there are .Net ways of stating the target url, please bear with me.
What I expect to happen is that if the visitor does not have the auth session cookie that Siteminder sets then they should be redirected to the SM auth flow and once authorized have this request complete.
Instead, what happens is that:
I use the get method: it fires and I get a 302 "Object Moved" response.
if I use the post method: it fires and I get a 200 Ok response but the returned payload is a small amount of html from SM saying that if I am not redirected to my destination shortly to press the button included in the form in that html. The jQuery fail promis fires though because it is expecting a JSON result, not html.
if I use put nothing happens.
I comment out my jQuery ajax call and just use a "location" redirect then SM will put up its challenge page; I can log in; and, the triggering request will be "continued" into a loop of length 3: it calls the page and fails with a 302 that seems to send the request back to SM where it is sent back to the target address to get a 302 then back to Sm then back to the target but it generates a 404 message.
I am deep in the weeds here. Advice would be wonderful
Oh, PS: running this in debug mode on my desktop (no SM) works. Running the Release version on the IIS dev server with SM is what fails.
EDIT
More info: after some additional siteminder config I started getting CORS violation messages. I am setting CORS headers now but that changes nothing. Siteminder seems to strip the CORS headers :/
Another thing I have noticed is that if i craft the failing GET request as a javascript location.href=url + "?" + queryStringData redirect everything works. Current jquery is all but depreciating setting async to false so crafting a non-async version is more than I want to tackle at the moment.
The local siteminder folks will file a ticket soon I think.
EDIT 2
I have ended up with a hacky "fix". I can not use standard GET, POST, PUT, etc methods to interact with the MVC methods because Siteminder is in the way. I have added CORS headers and have tried JSONP, none of that works in this case.
I have to use "redirects" instead. Setting location.href = "/usr?thing=foo&bar=baz" in the javascript functions then redirecting to the url as a result of the MVC methods.
This might be a Siteminder config issue. The local Siteminder mavens have submitted a ticket.
Your question still isn't clear what the problem is with each of the bullets you listed. Is the GET behavior what you expect? A 302 is just a redirect, is it the redirect you expect?
For "POST", you are seeing the "post preservation" behavior. Its what SiteMinder does so that if your session has timed out in the middle of filling out a form, you don't lose your work. Post preservation is a configuration parameter in the "Agent Configuration Object" in SiteMinder. It sounds like your SM admins have configured the ACO differently for the IIS server than they did the Linux server.
PUT - nothing happens? You don't get any response at all, the connection just hangs?
Your last bullet, with the redirect loop, this looping typically indicates that your user is logged in (authenticated) but not authorized, which is a SiteMinder policy configuration issue (again it sounds like different policies are being applied to your IIS server than the Linux)
HTH!
-Richard
I have an .NET application that uses mostly AngularJS, but for getting some things to work on IE9 we used sometimes a Jquery plugin to fix it (e.g. file upload)
Then on the other side there is an API that handles that application. But the API and the App won't be running on the same urls, so as good practice I would love to have 1 place where all the URl's can be set for where the API is where you can find the videos, ....
but how can I handle all this?
If i set them on the angular side, the controller is loaded after the JQuery,
so the value is undefined.
It can't read the web.config because it's just plain html files that are getting loaded. so no support from C#.
And didn't find any way on loading it in via JQuery.
Any suggestion on how to do this?
You can set it as a basic Object so scripts outside of Angular can use it. When you need to use it in the app, you can either require it directly or set it as a constant or create a service using it.
For example,
var api = {
baseUrl: 'www.moo.com/api/v1',
dogs: '/dogs',
cows: '/cows'
};
alert('This alert was created prior to angular module declartion with your api: ' + api.baseUrl);
angular.module('myCheeseIsGoodApp', [])
.constant('apiConstant', api)
.controller('TestController', ['$scope', 'apiConstant', function($scope, apiConstant){
$scope.apiConstant = apiConstant;
}]);
Here is a plunkr to check out how it works.http://plnkr.co/edit/fnqcwHxgxgrZlCpryMiC?p=preview
Keep in mind this was just a quick way to show you how to use it, but you may want to make it a bit more elegant.
We have an angular page that accesses a restful web service we are also generating. For the website we are making the routes match those of the web service for simplicity. For example accessing the website at http://myserver.com/books/book/1/chapter/2 would access the same route in the web service.
Now the slightly tricky part, if a list of books was returned then each book would contain a link to itself.
"links": [
{
"rel": "self",
"href": "http://myserver.com/books/book/1"
}]
The problem is in web api we use Url.Link to generate the href value. This though gives the server address as the web service and not the requesting angular website. We want to use these references to auto generate links on the web page itself. But they obviously just link straight back to the web service.
Generated: http://myWebServiceServer.com/books/book/1
Ideal: http://myWebSiteServer.com/books/book/1
Is there any nice function like Url.Link that the correct server could be specified?
To quote Scott Allen, "technically, you shouldn't use the Url property in an api controller to generate non-webapi links". I'm no expert on producing restful webapi's but it doesn't seem like a good design if the routing of the returned url is dependant on the routing of the calling website. Shouldn't the api just return information regarding books etc and let the website construct its own urls based on that information.
Regardless, the Url.Link you stated that you are using always returns an absolute url (i.e. http://myWebServiceServer.com/books/book/1).
If you insist on returning a url you could use Url.Route which can produce a relative url (i.e. /books/book/1). Your calling website can then determine the server to be added to the url.