Getting cors issue while sending post request from angular to pekko http server

Discussão em 'Angular' iniciado por Zaryab Ali, Outubro 19, 2024.

  1. Zaryab Ali

    Zaryab Ali Guest

    I'm implementing a scenario, in which there is an angular application which is showing counter variable on UI and a button (increment counter). When I click on button, it will send request to pekko http server (scala), where counter will be incremented and sent back to angular app. At last angular app will update the UI with the incremented counter.

    I've created simple pekko http server:

    import org.apache.pekko.actor.ActorSystem
    import org.apache.pekko.http.cors.scaladsl.CorsDirectives.cors
    import org.apache.pekko.http.scaladsl.Http
    import org.apache.pekko.http.scaladsl.model.StatusCodes
    import org.apache.pekko.http.scaladsl.server.Directives._
    import spray.json.DefaultJsonProtocol.IntJsonFormat
    import spray.json._

    import scala.concurrent.ExecutionContextExecutor

    object CounterServer extends App {
    implicit val system: ActorSystem = ActorSystem("my-system")
    implicit val executionContext: ExecutionContextExecutor = system.dispatcher

    val route = cors() {
    path("increment") {
    post {
    entity(as[String]) { jsonString =>
    val json = jsonString.parseJson.asJsObject
    val count = json.fields("count").convertTo[Int]
    val incrementedCount = count + 1

    val responseJson = JsObject("count" -> JsNumber(incrementedCount))

    complete(StatusCodes.OK, responseJson.toString())
    }
    }
    }
    }

    Http().bindAndHandle(route, "localhost", 8081)
    println("Server online at http://localhost:8081")
    }


    Then I ran this server in sbt-shell. Then I've created simple angular app:

    counter.component.html:

    <div style="text-align:center">
    <h1>Counter: {{ counter }}</h1>
    <button (click)="incrementCounter()">Increment</button>
    </div>


    counter.component.ts:

    counter: number = 0;

    constructor(private http: HttpClient) { }

    incrementCounter() {
    const url = 'http://localhost:8081/increment';
    this.http.post<any>(url, { count: this.counter })
    .subscribe(response => {
    this.counter = response.count;
    }, error => {
    console.error('Error occurred:', error);
    });
    }


    Then I've added this component in app.component.html and above is running perfectly fine.

    Now I'm trying to do same, but I want to serialize and deserialize via flatbuffers. For this firstly, I've created on counter.fbs file on scala side:

    counter.fbs:

    namespace FlatBufferExample;

    table Counter {
    count: int;
    }

    root_type Counter;


    Then I ran flatc --java counter.fbs on terminal which created necessary flatbuffers files. Then I've created a Service class:

    Service.scala:

    import com.google.flatbuffers.FlatBufferBuilder
    import counter.schema.FlatBufferExample.Counter

    import java.nio.ByteBuffer

    class Service {
    def serialization(flatBufferBuilder: FlatBufferBuilder, count: Int): Array[Byte] = {
    val counter = Counter.createCounter(flatBufferBuilder, count)

    flatBufferBuilder.finish(counter)

    flatBufferBuilder.sizedByteArray()
    }

    def deserialization(bufferData: Array[Byte]): Counter = {
    Counter.getRootAsCounter(ByteBuffer.wrap(bufferData))
    }
    }


    And then I've created Controller class:

    CounterController.scala:

    package counter.controller

    import com.google.flatbuffers.FlatBufferBuilder
    import counter.implementation.Service
    import counter.logfactory.LogFactory.logger
    import counter.schema.FlatBufferExample.Counter
    import org.apache.pekko.http.cors.scaladsl.CorsDirectives._
    import org.apache.pekko.http.cors.scaladsl.model.{HttpHeaderRange, HttpOriginMatcher}
    import org.apache.pekko.http.cors.scaladsl.settings.CorsSettings
    import org.apache.pekko.http.scaladsl.model.{HttpMethods, StatusCodes}
    import org.apache.pekko.http.scaladsl.model.headers.HttpOrigin
    import org.apache.pekko.http.scaladsl.server.Directives._
    import org.apache.pekko.http.scaladsl.server.Route
    import org.apache.pekko.util.Timeout

    import scala.collection.immutable
    import scala.concurrent.duration.DurationInt
    import scala.language.postfixOps

    class CounterController {
    implicit val timeout: Timeout = Timeout(180.seconds)

    val corsSettings: CorsSettings = CorsSettings.defaultSettings
    .withAllowedOrigins(HttpOriginMatcher(HttpOrigin("http://localhost:4200")))
    .withAllowedMethods(immutable.Seq(HttpMethods.POST, HttpMethods.OPTIONS))
    .withAllowedHeaders(HttpHeaderRange("Content-Type", "Authorization"))
    .withExposedHeaders(List("Content-Type"))


    val counterRoute: Route = concat(counter)

    def counter: Route = {
    cors(corsSettings) {
    path("counter-value") {
    post {
    entity(as[Array[Byte]]) { arrayByte =>
    logger.info("Received serialized value from client")

    val service: Service = new Service()

    val counterValue: Counter = service.deserialization(arrayByte)
    logger.info("Deserialized value is {}", counterValue.count())

    val incrementedCounter: Int = counterValue.count() + 1

    val builder = new FlatBufferBuilder(1024)
    val byteArray: Array[Byte] = service.serialization(builder, incrementedCounter)

    logger.info("Sending Incremented Serialized value to Client")
    complete(StatusCodes.OK, byteArray)
    }
    }
    }
    }
    }
    }


    Then I ran this server:

    ServerStartup.scala: package counter.server

    import counter.controller.CounterController
    import counter.logfactory.LogFactory.logger
    import org.apache.pekko.actor.ActorSystem
    import org.apache.pekko.http.scaladsl.Http

    object ServerStartup extends App {
    implicit val implicitActorSystem: ActorSystem = ActorSystem("Actor-System")
    val controller = new CounterController()

    Http().newServerAt("0.0.0.0", 8081).bind(controller.counterRoute)

    logger.info("Server online. Please navigate to http://0.0.0.0:8081")
    }


    Then I've created angular application, where I used same counter.fbs file and ran command flatc --ts counter.fbs which created necessary flatbuffer files. Then I created service in angular:

    counter.service.ts:

    import { Injectable } from '@angular/core';
    import { HttpClient, HttpHeaders } from '@angular/common/http';
    import { Observable, map } from 'rxjs';
    import { Counter } from '../../flatbuffers/flat-buffer-example/counter';
    import * as flatbuffers from 'flatbuffers';

    @Injectable({
    providedIn: 'root'
    })
    export class CounterService {
    private serverUrl = 'http://localhost:8081/counter-value';

    constructor(private http: HttpClient) {}

    serializeCounter(count: number): Uint8Array {
    const builder = new flatbuffers.Builder(1024);
    Counter.startCounter(builder);
    Counter.addCount(builder, count);
    const counter = Counter.endCounter(builder);
    builder.finish(counter);
    return builder.asUint8Array();
    }

    deserializeCounter(buffer: Uint8Array): number {
    const byteBuffer = new flatbuffers.ByteBuffer(buffer);
    const counter = Counter.getRootAsCounter(byteBuffer);
    return counter.count();
    }

    incrementCounter(counterValue: number): Observable<Uint8Array> {
    const headers = new HttpHeaders({ 'Content-Type': 'application/octet-stream' });
    const serializedData = this.serializeCounter(counterValue);

    return this.http.post(this.serverUrl, serializedData, {
    headers,
    responseType: 'arraybuffer',
    }).pipe(
    map((response: ArrayBuffer) => new Uint8Array(response))
    );
    }
    }


    Then I created component on angular side:

    counter.component.html:

    <div>
    <h2>Current Counter Value: {{ currentCounter }}</h2>
    <button (click)="incrementCounter()">Increment Counter</button>
    </div>


    counter.component.ts:

    import { Component } from '@angular/core';
    import { CounterService } from '../counter.service';

    @Component({
    selector: 'app-counter',
    templateUrl: './counter.component.html',
    styleUrls: ['./counter.component.css']
    })
    export class CounterComponent {

    currentCounter: number = 10;

    constructor(private counterService: CounterService) {}

    incrementCounter(): void {
    this.counterService.incrementCounter(this.currentCounter).subscribe(
    (response) => {
    const deserializedValue = this.counterService.deserializeCounter(response);
    this.currentCounter = deserializedValue; // Update the counter on the UI
    },
    (error) => {
    console.error('Error incrementing counter', error);
    }
    );
    }


    Now when I run ng serve angular application becomes live. When I click on increment button it is giving me cors error on angular side and IndexOutOfBoundsException on scala side:

    [​IMG]

    [​IMG]

    Same scala server, if I hit through scala client is working fine. Serialization and Desrialization is working fine with same counter.fbs schema file. But when I try to do same via angular app, it is giving this error. Guide me how can I tackle this?

    Continue reading...

Compartilhe esta Página