I Have asp.net web api project tageting 4.5.2
and an angular 10 web app
i'm trying to make a get call from angular to my api
conversion.service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class ConversionService {
constructor(private http: HttpClient) { }
getLayers () : Observable<string[]>
{
let uri = "http://localhost:44324/api/snapshots/getLayersFromShape";
return this.http.get<string[]>(uri);
}
}
mainpage.component.ts
import { Component, OnInit } from '#angular/core';
import { ConversionService } from '../service/conversion.service';
#Component({
selector: 'app-mainpage',
templateUrl: './mainpage.component.html',
styleUrls: ['./mainpage.component.css']
})
export class MainpageComponent implements OnInit {
filePath = 'C:\\Users\\MahammadM\\Downloads\\VS Shape files\\AREA2_OBSTACLES - EDITED';
layers: string[];
constructor(private conversionService : ConversionService) {
}
getLayers() {
this.conversionService.getLayers()
.subscribe(data => this.layers = data);
}
ngOnInit(): void {
}
}
mainpage.component.html
<button (click)="getLayers()">Click me!</button>
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var cors = new EnableCorsAttribute("https://localhost:4200", "*", "*");
config.EnableCors(cors);
var jsonFormatter = config.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.Remove(config.Formatters.XmlFormatter);
jsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
}
}
SnapshotsController.cs
[RoutePrefix("api/snapshots")]
public class SnapshotsController : ApiController
{
private readonly IVerticalStructureValidationService _validationService;
private readonly IConversionService _conversionService;
private readonly IShapeFile _shapeService;
public SnapshotsController(IConversionService conversionService, IVerticalStructureValidationService validationService, IShapeFile shapeService)
{
_validationService = validationService;
_conversionService = conversionService;
_shapeService = shapeService;
}
[HttpGet]
[Route("getLayersFromShape")]
public IHttpActionResult GetLayersFromShape(string filePath)
{
if (string.IsNullOrEmpty(filePath))
return Ok(new string[] { "no path provided" });
var layersAndShapePath = _shapeService.GetFileLayers(filePath);
return Ok(layersAndShapePath);
}
}
in this "getLayersFromShape" method I Have break point, but when i click my button on ui, nothing happens. console is empty.
what could be the issue?
i'm new to angular, any help is highly appreciated. thanks
Related
I'm running into an issue where Angular is unable to make a successful request on 2 of 3 endpoints but I have confirmed all work with postman and that the proxy is successfully routing the calls. Chrome - Network tab shows "blocked:other" and output of "ng serve" doesn't indicate anything in regards to proxy. Is there an issue with my angular service and/or server side controller?
Controller
[Route("api/[controller]")]
[ApiController]
public class AffiliationController : ControllerBase
{
public AffiliationController(
IQueryHandler<GetAffiliationsQuery, IQueryable<Affiliation>> getAffiliationsQuery,
ICommandHandler<ToggleAffiliationExclusionCommand> toggleAffiliationExclusionsCommand)
{
_getAffiliationsQuery = getAffiliationsQuery ?? throw new ArgumentNullException(nameof(getAffiliationsQuery));
_toggleAffiliationExclusionsCommand = toggleAffiliationExclusionsCommand ?? throw new ArgumentNullException(nameof(toggleAffiliationExclusionsCommand));
}
private readonly IQueryHandler<GetAffiliationsQuery, IQueryable<Affiliation>> _getAffiliationsQuery;
private readonly ICommandHandler<ToggleAffiliationExclusionCommand> _toggleAffiliationExclusionsCommand;
// Successfully Called
[HttpGet]
public async Task<ActionResult<PaginationResult<Affiliation>>> GetAffiliations([FromQuery] PaginationModel model)
{
var affiliations = await _getAffiliationsQuery.Handle(new GetAffiliationsQuery())
.PaginateAsync(model.PageIndex,
model.PageSize);
return affiliations;
}
// Not reached via Angular
[HttpGet("{id}")]
public async Task<ActionResult<Affiliation>> Foo(int id)
{
var target = await _getAffiliationsQuery.Handle(new GetAffiliationsQuery())
.SingleOrDefaultAsync(afn => afn.Id == id);
if (target == null)
{
return NotFound();
}
await _toggleAffiliationExclusionsCommand.HandleAsync(new ToggleAffiliationExclusionCommand(id));
return target;
}
}
Angular Service
export class AffiliationService {
private readonly apiUrl = 'api/affiliation';
constructor(
private http: HttpClient
) { }
public get(model: PaginationQuery): Observable<PaginationResult<Affiliation>> {
// Works just fine
return this.http
.get<PaginationResult<Affiliation>>(`${this.apiUrl}`, { params: UtilsService.buildQueryParams(model) })
.pipe(
catchError(this.handleError)
);
}
public toggle(affiliation: Affiliation) {
// Does not work unless I pass the 'id' as a query string parameter instead of a route parameter
return this.http
.get(`${this.apiUrl}/${affiliation.id}`)
.pipe(
catchError(this.handleError)
);
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(`Backend returned code ${error.status}, body was:`);
console.error(error.error);
}
return throwError('Something bad happened; please try again later.');
}
}
proxy.conf.json
{
"/api/*": {
"target": "http://localhost:5000",
"secure": false,
"logLevel": "debug"
}
}
NG Serve Output
Note
Changing the 'toggle' function to the following is able to successfully reach the endpoint. But I don't understand why this works over the other way as I can call the endpoint using the route param approach via Postman
public toggle(affiliation: Affiliation) {
return this.http
.get(`${this.apiUrl}`, { params: UtilsService.buildQueryParams({ id: affiliation.id }) })
.pipe(
catchError(this.handleError)
);
}
This looks like a CORS issue.
If you want to fix this without using a plugin, you can use the WebApi Cors package.
If using a C# ASP.NET CORE project:
Refer to the following page to enable CORS and the request can be
called.
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1
If using a C# WebAPI project:
Run the following command to install a CORS package for WebApi:
Install-Package Microsoft.AspNet.WebApi.Cors
Then you can use it as follows:
using System.Web.Http;
namespace WebService
{
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 }
);
}
}
}
and refer to the following URL for the full microsoft guide:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
I have a C# dll to query data using Entity Framework. After that I use owin to create a rest api to reference the dll to get data. I have two samples: one is use a console app to access the data, and it's ok. The other is I put the rest api inside a Windows service, and access it. It returns empty. I don't know why.
C# dll:
ef dll:
public class DemoContext:DbContext
{
private static DemoContext _instance;
public DemoContext() : base("MyDemoConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DemoContext, Configuration>());
}
public static DemoContext Instance
{
get
{
if (_instance == null)
{
_instance = new DemoContext();
}
return _instance;
}
}
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
}
owin api:
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
}
public class TeachersController : ApiController
{
[HttpGet]
[Route("api/teachers")]
public HttpResponseMessage Get()
{
try
{
var _context = DemoContext.Instance;
var teachers = _context.Teachers.ToList();
var response = Request.CreateResponse(HttpStatusCode.OK, teachers);
return response;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.OK, ex.Message);
}
}
}
console app:
class Program
{
static void Main(string[] args)
{
string baseAddress = "http://localhost:9000/";
// Start OWIN host
using (WebApp.Start<Startup>(url: baseAddress))
{
// Create HttpCient and make a request to api/values
/* HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(baseAddress + "api/values").Result;
var result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);*/
Console.ReadLine();
}
}
}
windowservice:
public partial class HostOwin : ServiceBase
{
private IDisposable owin;
public HostOwin()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
string baseAddress = "http://localhost:9000/";
owin = WebApp.Start<Startup>(url: baseAddress);
}
protected override void OnStop()
{
owin.Dispose();
}
}
I have Web API Project , it is referencing dll of database entities , it contain API Controllers , that return complex objects
when i test the Web API using visual studio (browser) or Telerik Fiddler before hosting the API on windows service , it is working fine and return a json of complex objects as expected .
After i create another project for windows service to self host the wep API using Owin
when i test any controller that return a complex object , it always return null while if i test any controller that return simple string , it is working fine , please help
I don't know why same things don't work after host on windows service
here my windows service startup.cs file
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
var assembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
string path = assembly.Substring(0, assembly.LastIndexOf("\\")) + "\\API.dll";
config.Services.Replace(typeof(IAssembliesResolver), new SelfHostAssemblyResolver(path));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
}
here is windows service class which inherit from service base class
public partial class selfHosting : ServiceBase
{
public string baseAddress = "http://localhost:9000/";
private IDisposable _server = null;
public selfHosting()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_server = WebApp.Start<Startup>(url: baseAddress);
}
protected override void OnStop()
{
if (_server != null)
{
_server.Dispose();
}
base.OnStop();
}
}
below is WebApiConfig File under another project (API)
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
}
after i check localhost:9000/api/employee
it return this line
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<ArrayOfEmployees xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Application.Model" i:nil="true"/>
Controller in web API Project
public class employeeController : ApiController
{
static readonly IRepository<employee> repository = new employeeRepository();
// GET api/<controller>
public IEnumerable<employee> Get()
{
return repository.GetAll();
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
if the get() method return simple string , it is working fine
for example
// GET api/<controller>
public string Get()
{
return "Hello" ;
}
Ok I'm really confused about this one. I have a angular2 app and it successfully does a http GET request to my localhost server. I implement a return type of IHttpActionResult with a return status of Ok and send some content back but when I look at the response on my console the content is blank but ti is retrieving the appropriate status code ie(200 ok). I need to find a way to send back json to my andular2 service.
Below is my WebAPI2 controller:
[Route("api/login")]
public class LoginController : ApiController
{
[HttpGet]
public IHttpActionResult Login()
{
//return Content(HttpStatusCode.OK, Json(new { success = true }));
//return Json(new { success = true });
List<int> myValues = new List<int>(new int[] { 1, 2, 3 });
return Ok(myValues);
}
}
My angular2 Service:
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map'
#Injectable()
export class AuthService {
isLoggedIn = false;
private _url = "http://localhost:61333/";
constructor(private _http: Http) { }
login(login) {
return this._http.get(this._url + "api/login").map(res => res.json());
}
logout() {
this.isLoggedIn = false;
}
}
And my angular2 component that is invoking my login function in my service
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { FormGroup } from '#angular/forms';
// Services
import { AuthService } from '../../_services/auth.service';
// Models
import { Login } from '../../_models/login.class';
#Component({
styles: [require('./login.component.css')],
template: require('./login.component.html')
//providers: [AuthService]
})
export class LoginComponent implements OnInit {
loginObj = new Login();
constructor(private _authService: AuthService, private _router: Router) {
}
login(form) {
this.loginObj.email = form.form._value.email;
this.loginObj.password = form.form._value.password;
this._authService.login(this.loginObj).subscribe(
value => { console.log("SUC:" + value); }
);
}
}
Thanks you.
Ross
According to this question I cannot immplement IHttpModule when self hsoting a web api on a windows service
Is there a way to add an httpModule when webApi is running with the HttpSelfHostServer?
However, I still need to add this code somewhere in my self hosted web api.
I found this blog about how to fix that:
http://www.silver-it.com/node/182
The code is as follows, however I can not have an IhttpModule implemented on aself hosted API
public class CORSPreflightModule : IHttpModule
{
private const string OPTIONSMETHOD = "OPTIONS";
private const string ORIGINHEADER = "ORIGIN";
private const string ALLOWEDORIGIN = "https://yourspodomain.sharepoint.com";
void IHttpModule.Dispose()
{
}
void IHttpModule.Init(HttpApplication context)
{
context.PreSendRequestHeaders += (sender, e) =>
{
var response = context.Response;
if (context.Request.Headers[ORIGINHEADER] == ALLOWEDORIGIN)
{
response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
}
if (context.Request.HttpMethod.ToUpperInvariant() == OPTIONSMETHOD && context.Request.Headers[ORIGINHEADER] == ALLOWEDORIGIN)
{
response.Headers.Clear();
response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
response.Headers.Add("Access-Control-Allow-Origin", "https://yourspodomain.sharepoint.com");
response.Headers.Add("Access-Control-Allow-Credentials", "true");
response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
response.Clear();
response.StatusCode = (int)HttpStatusCode.OK;
}
};
}
}
My self hosted web api is like this:
Program.cs
static void Main()
{
try
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new APIServiceTest()
};
ServiceBase.Run(ServicesToRun);
}
catch (Exception ex)
{
throw ex;
}
}
class Startup
{
// Hack from https://stackoverflow.com/a/17227764/19020 to load controllers in
// another assembly. Another way to do this is to create a custom assembly resolver
//Type valuesControllerType = typeof(OWINTest.API.ValuesController);
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public void Configuration(IAppBuilder appBuilder)
{
try
{
//Debugger.Launch();
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.Add(new CustomHeaderHandler());
var corsAttr = new EnableCorsAttribute(System.Configuration.ConfigurationManager.AppSettings["DominioSharePoint"].ToString(), "*", "*");
config.EnableCors(corsAttr);
// Enable attribute based routing
// http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
catch (Exception ex)
{
throw ex;
}
}
}
My controller:
[EnableCors(origins: "https://xx.sharepoint.com", headers: "*", methods: "*")]
public class CuentasCobroController : ApiController
{
However because its self hosted I cant implement an IHttpModule there as explained above, but I can create a custom handler how can I implemente the code above from the blog in the custom handler?
public class CustomHeaderHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith((task) =>
{
HttpResponseMessage response = task.Result;
response.Headers.Add("Access-Control-Allow-Origin", "*");
return response;
});
}
}
Question is, how can I integrate first code, into the startup of my windows service?
You are almost there by using DelegatingHandler instead of IHttpModule.
config.MessageHandlers.Add(new CorsHeaderHandler());
DelegatingHandler.SendAsync can access both request and response.
public class CorsHeaderHandler : DelegatingHandler
{
private const string OPTIONSMETHOD = "OPTIONS";
private const string ORIGINHEADER = "ORIGIN";
private const string ALLOWEDORIGIN = "https://yourspodomain.sharepoint.com";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var allowedOrigin = request.Headers.Any(t => t.Key == ORIGINHEADER && t.Value.Contains(ALLOWEDORIGIN));
if (allowedOrigin == false) return task.Result;
if (request.Method == HttpMethod.Options)
{
var emptyResponse = new HttpResponseMessage(HttpStatusCode.OK);
emptyResponse.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
emptyResponse.Headers.Add("Access-Control-Allow-Origin", ALLOWEDORIGIN);
emptyResponse.Headers.Add("Access-Control-Allow-Credentials", "true");
emptyResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
return emptyResponse;
}
else
{
task.Result.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
task.Result.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
return task.Result;
}
});
}
}
Simply put, you can't use IHttpModule with self-hosted Web API, or anything that is not IIS. IHttpModule is an IIS concept only.
What you can do instead, as you have discovered, is you can modify the Web API pipeline and insert your code (or the Web API equivalent) there instead. This could be done with a DelegatingHandler, or an action filter.
The simplest solution, however, would be to simply use the Microsoft.AspNet.WebApi.Cors NuGet package. With an HttpConfiguration object, call .EnableCors(...) and pass in an EnableCorsAttribute object as per the instructions here from Microsoft.
This is what you've already done in your code above, but you seem to be trying to also add in the custom CORS code from your HTTP module. If you remove the EnableCors attribute from your controller, and remove your CustomHeaderHandler, it should work as you would expect.