UI frameworkleri ve HTTP Requestleri

Selamlar. UI Frameworkleri hakkında anlayamadığım bir konu var.
Örneğin Angular framework kullanarak yapılmış bir arayüzüm var. Bu arayüzde bir buton var ve butona tıklandığında kendi yazdığım bir API’dan ne olduğu önemli olmayan bir veriyi istiyor. Bu API’a giden istek Angular’ın çalıştığı serverdan mı gider yoksa kullanıcının kendi makinesinden mi? Yani API’ın çalıştığı makinenin loglarına baksam request’in geldiği IP, Angular app’in çalıştığı makinenin IP’si mi olur yoksa kullanıcının IP’si mi?

Soruyu tam olarak nasıl soracağımı bilemediğimden biraz saçmalamış olabilirim. Şimdiden kusuruma bakmayın.

Edit: Benim fikrim kullanıcının ipsinin gözükeceği yönünde ancak takım liderim ip’yi direkt backend’den alırsan anguların çalıştığı makinenin ip’si çıkar öyle yapma diyor. Önce angular’a gelen isteğin ipsini al sonra bunu backend’e yolla diyor.

Angular kodunu calistiran taraf kullanicinin tarayicisi oldugu icin, Javascript’le orada yaptigin her request kullanicinin makinesinden yapilir. Angular’i yayinlayan, internete acan makinanin tek isi bir istek geldiginde calistirilacak javascript kodunu kullaniciya vermek. O javascript kodunu calistiran makine o istegi yapar yani.

Ama karsi makinenin IP’sini ogrenmek zaten gelen HTTP istegine bakilarak yapilabilir.

Illa server-side renderer’a kod eklemeyecegim derseniz dediginiz gibi IP adresini JSON’da saglayan bir servise javascript’ten istek atip IP adresini client-side’da ogrenebilirsiniz:

    fetch('https://api.ipify.org?format=json')
      .then(response => response.json())
      .then(data => console.log(data.ip))
      .catch(error => console.log(error));
1 Beğeni

Ben söylediğim gibi olduğuna emindim fakat takım liderim söylediği gibi olduğu konusunda ısrarcıydı. daha sonra söylediğim gibi olduğu konusunda anlaştık (birkaç kanıttan sonra). kullanıcının ip’sini direkt olarak api’a gelen istek üzerinden alacağız artık. teşekkürler yanıt için.

Hımm,

Merhabalar,

Bu programlama jargonlarına çok fazla hakim değilim. Ben de son zamanlarda flask ile kendi kişisel websitemi geliştiriyordum. Bu site için bir comment-like sistemine ihtiyaç duydum. Aradım ve Flask-Comments gibi bir kütüphane buldum. Ama tasarımı hoşuma gitmedi. Ben de kendi yorum-beğeni sistemimi yazmak istedim.

Tasarlamam gereken yorum-beğeni sistemi nasıl olmalıdır sorusuna şöyle yanıtlar verirken buldum kendimi:

  1. Bir yorumu veya beğeniyi oluşturmak istediğimiz zaman, tam sayfa yenilenmesinin yaşanmaması gerekir. Örnek: Youtube’da yorum yaptığınız zaman sayfa yenilenmez, html içeriği dinamik olarak oluşturulur.
  2. HTML sayfasında mevcut olan bir yorumun veya beğeninin değiştirilme işlemi esnasında sayfanın tekrar yüklenmemesi gerekir.
  3. Mevcut bir sayfadaki bir html içeriği eğer bütün html içeriğinden çıkarılacaksa, sayfanın yeniden yüklenmemesi gerekir.
  4. Sayfa açılırken, hangi html yapılarının değiştirilebilir, hangi yapıların değiştirilemez olduğu, istek gönderen kullanıcının kimliğine göre belirlenir.

Bu projede AngularJS kullanmadım hiç. Frontend kısmını js ile, backend kısmını ise Flask ile hallediyorum. Yorumu meydana getiren html içeriği javascript dosyasında bir class olarak tutuluyor. card isimli, biçimi yorum olmaya müsait bir bootstrap html etiket grubu bu. Özellikleri, hem kullanıcıya ait olan bir takım kişisel verilerle, hem de sınıfa ait genel özelliklerle belirleniyor. Yorumların ve beğenilerin olduğu templatelerde full-page refresh özelliğini kaldırmak için istek formunu Flask-WTF formlarını kullanarak değil doğrudan javascript kullanarak oluşturuyorum. Eminim, şu anda yaptığım işlemleri kolaylaştıran araçlar geliştirilmiştir.

Tasarladığım uygulamada, kullanıcı, current_user olarak sayfaya giriş yaparken, haberi dahi olmadan, javascript yoluyla otomatik olarak backend tarafına istek tipini gönderir. backend kısmında bu istek tipi ve current_user bilgisi kullanılarak, sayfada bulunacak olan bütün yorumların ve beğenilerin user niteliiklerinin bu current_user değerine eşit olup olmadığı öz-yinelemeli bir şekilde sorgulanır, sonra da bu sorgulamaya göre belirli kısımları kullanıcı tarafından değiştirilebilir olan bir html içeriği oluşturulur. Bütün bu işleme dinamik html yapıları oluşturma süreci de diyorlar. current_user, anonim olduğunda yorumlar ve beğeniler yine yüklenir ama yüklenen yorumların mesela Update ve Delete buttonları görünmez.

Özetle, genel olarak kullanıcıyla temas frontend kısmında gerçekleşir, kullanıcının istediği bilginin ne olduğu da bu aşamada backend tarafına gönderilir (Request) . Gönderilen bilgi backend tarafında, current_user’a göre sorgulanır sonra da current_user’a göre olan bilgileri içeren bir json Response döner, doğrulanamazsa da başka bir status code’a sahip bir Response döner. Sonra da bu json tipindeki cevaba (response’a) göre html içeriğinin nasıl oluşması gerektiğini belirleriz.

Her bir article ve yorum için bir tane card nesnesi oluşturulur. Nesnelerin özellikleri ise, oluşma esnasında veritabanındaki tabloların birbirleriyle olan ilişkilerine göre düzenlenir. Article için Card nesnesinin sadece footer kısmı, yorum için ise header, body ve footer kısımları aktif olur.

Her bir article için çalışacak sayfa:

article.html

{% extends "base.html" %}
{% block content %}
    {% include "view.html" %}
    {% block comment_block %}
        <script>
            init_card();
            init_comments();
        </script>
    {% endblock comment_block %}
{% endblock content %}

main.js’den gelen kurucu fonksiyonlar.

function init_card() {
    var div = document.createElement("div");
    div.id = `comments-${title}`;
    document.body.append(div);
    var card = new Card(
        primary_id=title,
        secondary_id="secondary",
        content="",
        src="",
        href="",
        date="",
        username="",
        like="",
        dislike=""
    );
    card.footer();
}
function init_comments() {
    var form = new FormData();
    form.append("comments", true)
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if ((xhr.status === 200) && (xhr.readyState == XMLHttpRequest.DONE)) {
            var comments = JSON.parse(xhr.responseText);
            if (comments.length > 0) {
                recursively_init_comments(comments, `${title}-secondary`)
            }
            article_stats(comments);
        }
    }
    xhr.open("POST", `/article/${title}`, true);
    xhr.send(form);
}
function recursively_init_comments(obj, primary_id) {
    if (obj.length > 0) {
        document.getElementById(`span-comments-${primary_id}`).innerHTML = `(${obj.length})`;
    }
    for (var child of obj) {
        create_card(primary_id=primary_id, comment=child);
        if (child["children"].length > 0) {
            recursively_init_comments(child["children"], `${primary_id}-${child.id}`);
        }
    }
}

function article_stats() {
    if (article_like_count > 0) {
        document.getElementById(`span-like-${title}-secondary`).innerHTML = `(${article_like_count})`;
    }
    if (article_dislike_count > 0) {
        document.getElementById(`span-dislike-${title}-secondary`).innerHTML = `(${article_dislike_count})`;
    }
}
function create_card(primary_id, comment) {
    var card = new Card(
        primary_id=primary_id,
        secondary_id=comment.id,
        content=comment["content"],
        src=comment["src"],
        href=comment["href"],
        date=comment["date"],
        username=comment["username"],
        like=comment["likes"],
        dislike=comment["dislikes"]
    );
    card.header();
    card.body();
    card.footer();
    document.getElementById(`hidden-value-${primary_id}-${comment['id']}`).value = comment["hidden_value"];
    if (comment["likes"] > 0) {
        document.getElementById(`span-like-${primary_id}-${comment['id']}`).innerHTML = `(${comment["likes"]})`;
    }
    if (comment["dislikes"] > 0) {
        document.getElementById(`span-dislike-${primary_id}-${comment['id']}`).innerHTML = `(${comment["dislikes"]})`;
    }
    if (comment["username"] != comment["current_user"]) {
        document.getElementById(`btn-update-${primary_id}-${comment['id']}`).style.display = "none";
        document.getElementById(`btn-delete-${primary_id}-${comment['id']}`).style.display = "none";
    }
}

Card nesnesinin kodları ve yorumlar üzerinde yapılan işlemler için yazılmış olan javascript ve python fonksiyonlarını paylaşmıyorum. Sadece şunu söyleyebilirim ki, bir model üzerinde yapılmak istenen işlem için, backend tarafında bir python fonksiyonu, frontend tarafında da bir javascript fonksiyonu çalışıyor. Javascript fonksiyonu Python’a bir istek gönderiyor, Python fonksiyonu ise Javascript’e bir yanıt veriyor.

Backend tarafında, javascriptten yapılan isteklere göre bir takım yardımcı fonksiyonlar kullanılarak işlemler yapılır:

Backend:

@articles.route("/article/<string:article_title>", methods=["GET", "POST"])
def article(article_title):
    a = Article.query.filter_by(title=article_title).first_or_404()
    if "comments" in request.form:
        return response_children(a, request.url_root)
    if request.method == "POST":
        if current_user.is_authenticated:
            if "add" in request.form:
                return add_comment(request, db, a)
            elif "delete" in request.form:
                return delete_comment(request, db, a)
            elif "update" in request.form:
                return update_comment(request, db)
            elif "like_dislike" in request.form:
                return like_dislike_comment(request, db, a)
        else:
            return Response("", 404)
    if request.method == "GET":
        if a:
            data = {"title": a.title}
        else:
            data = {"title": None}
        Response(json.dumps(data), 201)
    return render_template(
        "articles/article.html",
        title=a.title,
        articles=[a],
        primary_id=a.id
    )

Backend kısmında çalışan ve sayfa açılır açılmaz kullanıcının sadece belirli yerlerini “değiştirebileceği” html içeriklerinin sunulmasında rol alan yardımcı fonksiyonlardan bir tanesi şu:

def response_children(model, url_root):
    data = find_children_recursively(orphan_comments(model), url_root)
    return Response(json.dumps(data), 200)

Bu anlattığım, html sayfası açılırken gerçekleşen bir işlem bu arada. Sayfa içinde yapılabilecek add_comment, update_comment, delete_comment, like_dislike_comment gibi değişiklikler için de benzer işlemler uygulanır.

2 Beğeni

hocam yanıt için teşekkürler gün içerisinde ortak noktada buluşmuştuk zaten takım liderimle.
ayrıca

xmlhttprequest yerine FetchAPI kullanırsanız işiniz çok daha kolay olur. Kolay gelsin.

1 Beğeni

Rica ederim. Aslında yazdığım mesaja ihtiyaç yoktu. Aşka geldim yazdım. :slight_smile:

Bu da kolayıma geliyor, vanilla javascript yazarak ilerlemek istemiştim. Öyle de kaldı.