Angular 2 Not Deserializing .NET List<T> - json

I'm having problems deserializing a .NET List into an Angular 2 array. I keep receiving an error:
ERROR Error: Cannot find a differ supporting object...NgFor only supports binding to Iterables such as Arrays.
I've checked here but none of the proposed solutions have been working for me: https://github.com/angular/angular/issues/6392
C#
Model
public class Filter
{
public string filterType { get; set; }
public string filterKey { get; set; }
public string filterValue { get; set; }
}
Controller Action
public List<Filter> Filters { get; set; } = new List<Filter>()
{
new Filter()
{
filterType = "TypeA",
filterValue = "ValueA",
filterKey = "TypeA|ValueA"
},
new Filter()
{
filterType = "TypeB",
filterValue = "ValueB",
filterKey = "TypeB|ValueB"
}
};
// GET api/values
[HttpGet]
public ActionResult Get()
{
var response = JsonConvert.SerializeObject(Filters);
return new JsonResult(response);
}
I have confirmed with both POSTMAN and Chrome Developer Tool's that this controller is correctly returning the JSON:
[{"filterType":"TypeA","filterValue":"TypeA","filterKey":"TypeA|ValueA"},
{"filterType":"TypeB","filterValue":"ValueB","filterKey":"TypeB|ValueB"}]
Angular
Model (filter.ts)
export class Filter{
filterType: string;
filterKey: string;
filterValue:string;
}
Service (filter.service.ts)
#Injectable()
export class FilterService {
private apiUrl: string = "http://localhost:7639/api/filters";
constructor(private http: Http) { }
public getFilters = (): Observable<Filter[]> => {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get(this.apiUrl,options)
.map(res => <Filter[]>res.json())
.do(x => console.log(x)) <-- This clearly logs the JSON
.catch(this.handleError);
}
private handleError(error:Response){
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
Component (filter.component.ts)
export class FilterComponent implements OnInit{
title = 'Filters';
public filters: Filter[];
constructor(private filterService: FilterService) {
}
ngOnInit() {
this.getFilters();
}
private getFilters(){
this.filterService.getFilters().subscribe(filters => {
this.filters = filters;
console.log(filters);
},
error => {
console.log(error);
}, () => {
});
}
}
Component HTML (filter.component.html)
<h1>{{title}}</h1>
<div *ngFor="let filter of filters">
<p>{{filter.filterType}}</p>
<p>{{filter.filterValue}}</p>
<p>{{filter.filterKey}}</p>
</div>
Any help with this would be appreciated

The answer was super simple.
// GET api/values
[HttpGet]
public ActionResult Get()
{
var response = JsonConvert.SerializeObject(Filters);
return new JsonResult(response);
}
I was doing redundant serialization on the list and passing the response back as a string.
Changing the above method corrected the issue:
// GET api/values
[HttpGet]
public ActionResult Get() => new JsonResult(Filters);

Related

create a Custom Exception Handler Middleware generating response based on client request

How to create a Custom Exception Handler Middleware. This middleware should generate a custom response based on the Calling Client. If the client is requesting via AJAX then the response should be a JSON Response describing the Error otherwise Redirect the client to Error page.
Controller code
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
middleware code
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception error)
{
var response = context.Response;
var customError = new CustomError();
switch (error)
{
case AppException e:
// custom application error
customError.StatusCode = (int)HttpStatusCode.BadRequest;
break;
case KeyNotFoundException e:
// not found error
customError.StatusCode = (int)HttpStatusCode.NotFound;
break;
default:
// unhandled error
customError.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
customError.ErrorMessage = error?.Message;
if (context.Request.ContentType == "application/json;")
{
var result = JsonSerializer.Serialize(customError);
await response.WriteAsync(result);
}
else
{
context.Response.Redirect("/Errors/CustomError");
}
}
}
Custom Error class code
public class CustomError
{
public int StatusCode { get; set; }
public string ErrorMessage { get; set; }
}
Error View model
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
you could add the code in your startup class:
app.UseStatusCodePagesWithReExecute("/errors/{0}");
add a controller(In my case I tested with HttpContext.Request.Headers["Content-Type"] ,it should be context.Request.ContentType == "application/json;" for MVC project ):
public class ErrorsController : Controller
{
[Route("errors/{statusCode}")]
public IActionResult CustomError(int statusCode)
{
if (HttpContext.Request.Headers["Content-Type"] == "application/json")
{
var cuserr = new CustomError() { ErrorMessage = "err", StatusCode = statusCode };
return new JsonResult(cuserr);
}
else
{
if (statusCode == 404)
{
return View("~/Views/Errors/404.cshtml");
}
return View("~/Views/Errors/500.cshtml");
}
}
}
and the views:
The result:
for more details,you could read the offcial document:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0

Angular & RxJs : How to map json to Object Array

I try to map a Json http request answer to an object array (User) using RxJs :
My Json data looks like :
{"#context":"\/cotabe\/api\/contexts\/User","#id":"\/cotabe\/api\/users","#type":"hydra:Collection","hydra:member":[{"#id":"\/cotabe\/api\/users\/1","#type":"User","id":1,"email":"a.a#gmail.com","firstname":"Aaa","lastname":"Ggg","phone":"0606060606","mobile":"0606060607","fullName":"Aaa Ggg","username":"a.a#gmail.com","roles":["ROLE_DEVROOT","ROLE_USER"],"password":"$argon2i","createdAt":"-0001-11-30T00:00:00+01:00","updatedAt":"-0001-11-30T00:00:00+01:00","deleted":false}],"hydra:totalItems":1}
I would like to extract from that a User[], with user model :
export class User {
constructor(
public id: number,
public email: string,
public firstname: string,
public lastname: string,
public phone: string,
public mobile: string,
public roles: string[],
) {}
}
In my user service I have :
export class UserService {
private users: User[] = [];
userSubject = new Subject<User[]>();
constructor(private apiService: ApiService) { }
emitUsers() {
this.userSubject.next(this.users.slice());
}
getUsersFromRest() {
this.apiService.getEntriesFromRest('users').subscribe(
(data: User[])=>{
this.users = data['hydra:member'];
});
this.emitUsers();
}
}
with in an api service
public getEntriesFromRest (option: string): any {
return this.httpClient.get<any[]>(this.baseEndpoint + option);
}
I know it is an rXjs operator stuff, but I did not manage to find the solution.
Thank you for your help,
export class UserService {
userSubject = new Subject<User[]>();
userSubject$ = this.userSubject.asObservable();
constructor(private apiService: ApiService) {}
getUsersFromRest() {
this.apiService
.getEntriesFromRest("users")
.pipe(
map(x => JSON.stringify(x)),
map(x => JSON.parse(x)),
pluck("hydra:member")
)
.subscribe((data: User[]) => {
this.usersSubject.next(data);
});
}
}
Can you try the above code
export class UserService {
private userSubject = new Subject<User[]>();
userSubject$ = this.userSubject.asObservable(); // If you add a public observable of your subject, you can have other components subscribe to this, and not be able to alter the subject, but still get the data.
constructor(private apiService: ApiService) { }
getUsersFromRest() {
this.apiService.getEntriesFromRest('users')
.pipe(
map((x: any) => JSON.parse(x)) // Convert your response from JSON to an Object
.subscribe(
(data: User[]) => {
this.usersSubject.next(data.hydra.member);
});
}
}
There is no need to have a separate emit users method.

How to map an angular 2 class from an http call

I'm new in Angular.
I've a class called User:
export class User {
private id: number;
private name: string;
private surname: string;
get Id(): number {
return this.id;
}
set Id(newId: number) {
this.id = newId;
}
get Name(): string {
return this.name;
}
set Name(newName: string) {
this.name = newName;
}
get Surname(): string {
return this.surname;
}
set Surname(newSurname: string) {
this.surname = newSurname;
}
}
...a function to retrive an array of user:
getValues() {
this.usersService.getUsers()
.subscribe((users: User[]) => this.dataSource = users);
}
and a method to retrive the users array from backend WebApi:
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.usersSearchUrl)
.pipe(
tap(users => this.log(`fetched users`)),
catchError(this.handleError('getUsers', []))
);
}
finally the json returned from the webapi:
[{"id":"1","name":"Alberico","surname":"Gauss"},{"id":"2","name":"Anassimandro","surname":"Dirac"},{"id":"3","name":"Antongiulio","surname":"Poisson"}]
I would have expected that the call would automatically mapped the User class, instead it only gives me an array of type User, in fact if I write something in my component .subscribe((utenti: Utente[]) => console.log(utenti[0].Surname)); the console writes me "undefined". Can you tell me where I'm wrong? Thanks
You are retrieving JSON from your backend, as is expected. A Javascript (or typescript) class is not the same thing.
When the JSON is returned, it can be automatically converted into a simple JSON object in Javascript but it will NOT include all your getters and setters. So these class methods are not available, which is why you get undefined.
Remove all the getters and setters and add a constructor. Then you can just call Surname directly as a property and it will return the value (since it will then just be a plain JSON object).
export class User {
constructor() {
}
public id: number;
public name: string;
public surname: string;
}
Or without a constructor, and just declare the properties directly:
export class User {
public id: number;
public name: string;
public surname: string;
}
Or you could also use an interface:
export interface User {
id: number;
name: string;
surname: string;
}
You can read more about this issue here and here.
I think in component ts use like this code:
users: User[];
constructor(
private us: usersService,
public auths: AuthService
)
this.us.getUsers.subscribe(
users=> {
this.users= users.map((user) => {
return new User(user);
});
}
);
In service I think to write:
public getUsers(): Observable<User[]> {
let headers = new Headers();
headers.append('x-access-token', this.auth.getCurrentUser().token);
return this.http.get(Api.getUrl(Api.URLS.getUsers), {
headers: headers
})
.map((response: Response) => {
let res = response.json();
if (res.StatusCode === 1) {
this.auth.logout();
} else {
return res.StatusDescription.map(user=> {
return new User(user);
});
}
});
}
For me this logic work perfect. I hope to help you with this code

JSON response from spring boot rest controller getting repeated

I was trying to build a rest api using Spring boot 1.5.9.RELEASE and been stuck on this issue. The post request to api end points works just fine but when comes to get requests the result gets repeated. The response which the app produces for get request is
{"data":["Administrator"]}{"data":["Administrator"]}
The associated request mapping class code
#RequestMapping("/get")
public ResponseEntity getAllRoles()throws Exception{
List<Roles> roles = rolesService.getRoles();
Set<String> roleNames = new HashSet<>();
for(Roles r : roles)
roleNames.add(r.getRoleName());
return new ResponseEntity(new Response(roleNames), HttpStatus.OK);
}
The Response class
public class Response<T> {
private T data;
public Response() {}
public Response(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Any ideas about how to solve the issue? Thanks in advance
You are creating response twice, use below
RequestMapping("/get")
public ResponseEntity<?> getAllRoles()throws Exception{
List<Roles> roles = rolesService.getRoles();
Set<String> roleNames = new HashSet<>();
for(Roles r : roles)
roleNames.add(r.getRoleName());
return new ResponseEntity<Object>(roleNames, HttpStatus.OK);
}
Injecting #JsonProperty("yourFiledName") at the getter method works for me.
`
public class Response {
private T data;
public Response() {}
public Response(T data) {
this.data = data;
}
#JsonProperty("data")
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
`

POST List<object> from Angular2 Service to SpringBoot RestController

enter image description hereHi I am actually making an application in Angular2(Frontend) and Springboot(Backend).
Uptill now , I have made many request from angular2 to Springboot i.e GET ;POST .I have even sent single Json object from Angular2 service to Springboot .
But currently I have a scenario where I need to send List of Json object from Angular2 to Springboot Rest controller .but unfortunately I am unable to do that .
what I am doing is
1: I have made an array of Type for which I need to send object in angular2 .
2:Pushed individual json object of same txpe into that array
3.Passed that array to service of angular2 in function call.
4.Finally made a POST request to Springboot Controller
below is my function to add JSon object to array :
updateChecked(value, event) {
// console.log(event.target.getAttribute('value'));
if (event.target.checked) {
// this.checkArr.push(event.target.getAttribute('value'));
this.checkArr.push({
profil_id: event.target.getAttribute('value'),
profil_name_lang: null,
profil_name_short: null,
geschaeftsbereich: null,
spezialberatung: null
});
} else if (!event.target.checked) {
//let indexx = this.checkArr.indexOf(event.target.getAttribute('value'));
let indexx = this.checkArr.findIndex(_profile => _profile.profil_id === event.target.getAttribute('value'));
this.checkArr.splice(indexx, 1);
console.log(indexx);
}
}
below is my Angular2 service function
private _deleteURL = 'http://localhost:9000//deleteprofile'
private headers1 = new Headers({'Content-Type' : 'application/x-www-form-urlencoded'});
deleteProfile(chckArr: Profile[]): Promise < Profile > {
// console.log("karan in delete");
console.log(chckArr);
return this.http
.post(this._deleteURL, chckArr, {
headers: this.headers1
})
.toPromise()
.then(res => res.json().data as Profile)
.catch(this.handleError);
}
below is my Springboot Funtion :-
#RequestMapping(method = RequestMethod.POST, value = "/deleteprofile")
public void deleteProfile(#RequestBody List < Profile > profile) { // function to delete an existing profiles
try {
// profileservice.deleteProfile(profile);
} catch (Exception e) {
e.printStackTrace();
}
}
entity class in Angular2
export class Profile {
profil_id: string;
profil_name_lang: string;
profil_name_short: string;
geschaeftsbereich: string;
spezialberatung: string;
}
entity class in Springboot
package permissionbackendservice.permissionmatrix.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
#Entity
//#IdClass(ActivityId.class)
#Table(name = "T_DM_VM_PROFILE_DIM")
public class Profile implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1 L;
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROFIL_ID")
private String profil_id = null;
#Column(name = "PROFIL_NAME_LANG")
private String profil_name_lang = null;
#Column(name = "PROFIL_NAME_KURZ")
private String profil_name_short = null;
#Column(name = "GESCHAEFTSBEREICH")
private String geschaeftsbereich = null;
#Column(name = "SPEZIALBERATUNG")
private String spezialberatung = null;
public String getProfil_id() {
return profil_id;
}
public void setProfil_id(String profil_id) {
this.profil_id = profil_id;
}
public String getProfil_name_lang() {
return profil_name_lang;
}
public void setProfil_name_lang(String profil_name_lang) {
this.profil_name_lang = profil_name_lang;
}
public String getProfil_name_short() {
return profil_name_short;
}
public void setProfil_name_short(String profil_name_short) {
this.profil_name_short = profil_name_short;
}
public String getGeschaeftsbereich() {
return geschaeftsbereich;
}
public void setGeschaeftsbereich(String geschaeftsbereich) {
this.geschaeftsbereich = geschaeftsbereich;
}
public String getSpezialberatung() {
return spezialberatung;
}
public void setSpezialberatung(String spezialberatung) {
this.spezialberatung = spezialberatung;
}
}
Error:
Change your Controller deleteProfile() method using an array for the #RequestBody like this :
#RequestMapping(method = RequestMethod.POST, value = "/deleteprofile")
public void deleteProfile(#RequestBody Profile[] profile) {
// function to delete an existing profiles
try {
// profileservice.deleteProfile(profile);
} catch (Exception e) {
e.printStackTrace();
}
}
If the profileservice.deleteProfile() require a list of profile instead of an array you can get it using Arrays.asList(profile)
On the client side, you have to send application/json request. So update your client request by deleting the header :
private _deleteURL = 'http://localhost:9000/deleteprofile'
deleteProfile(chckArr: Profile[]): Promise < Profile > {
// console.log("karan in delete");
console.log(chckArr);
return this.http
.post(this._deleteURL, chckArr)
.toPromise()
.then(res => res.json().data as Profile)
.catch(this.handleError);
}
It's not an issue but you forget trailing "/" in your url too.
UPDATE WITH WORKING CODE :
Rest controller in Person microservice :
#RestController
#RequestMapping("/api")
public class CustomController {
private static Logger log = LoggerFactory.getLogger(CustomController.class);
#Autowired
PersonService personService;
#RequestMapping(method = RequestMethod.POST, value = "/deleteperson")
public ResponseEntity deletePerson(#RequestBody Person[] persons) {
log.info(" Batch delete " + persons);
personService.delete(Arrays.asList(persons));
return ResponseEntity.ok().build();
}
}
Angular service in the gateway :
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import {Person} from './person.model';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class CustomService {
constructor(private http: Http) {
}
batchDelete(persons: Person[]): Observable<any> {
return this.http.post('/person/api/deleteperson', persons);
}
}
Delete method int person.component.ts:
batchDelete() {
console.log('--- delete ---- ' + JSON.stringify(this.people));
this.customService.batchDelete(this.people).subscribe( (res) => {
this.eventManager.broadcast({ name: 'personListModification', content: 'Delete them all'});
});
}
CODE ON GITHUB
Results :
BEFORE
AFTER DELETE