/Users/song/codelearn/FastAPI-Oracle-main/main.py
import os, secrets, requests, json from typing import List, Optional from fastapi import Depends, FastAPI, HTTPException, Response, status, Path from fastapi.security import HTTPBasic, HTTPBasicCredentials from starlette.status import HTTP_404_NOT_FOUND, HTTP_401_UNAUTHORIZED, HTTP_503_SERVICE_UNAVAILABLE from sqlalchemy.orm import Session from sqlalchemy import MetaData, inspect from sqlalchemy.sql import func from app import model, schema, crud from app.database import SessionLocal, engine from dotenv import load_dotenv, find_dotenv app = FastAPI() def get_db(): db = SessionLocal() try: yield db finally: db.close() @app.on_event("startup") async def start( db: Session = Depends(get_db) ): print("Starting up...") inspector = inspect(engine) # check if we created the tables in the database already. If not, create and populate them if not inspector.has_table('customer'): print("Creating the tables in the database") model.LoyaltyLevel.metadata.create_all(engine) model.Customer.metadata.create_all(engine) model.Purchase.metadata.create_all(engine) #populate the tables with test data print("Populating the tables in the database") session = Session(engine) loyalty_level_instance_1 = model.LoyaltyLevel(level_id="pl", description='Platinum', discount=25) loyalty_level_instance_2 = model.LoyaltyLevel(level_id="gl", description='Gold', discount=15) session.add_all([loyalty_level_instance_1, loyalty_level_instance_2]) customer_instance = model.Customer(firstname='John', lastname='Doe', date_of_birth=func.now(), level_id=loyalty_level_instance_1.level_id, signup_date=func.now()) purchase_instance = model.Purchase(customer = customer_instance, purchase_name="something") session.add_all([purchase_instance]) session.commit() else: print("Found the database tables") @app.on_event("shutdown") async def shutdown(db: Session = Depends(get_db)): print("Shutting down...") print("Dropping tables") model.LoyaltyLevel.metadata.drop_all(engine) model.Customer.metadata.drop_all(engine) model.Purchase.metadata.drop_all(engine) print("Tables dropped") @app.get("/") def read_root(): return {"Hello": "World"} # -- Customer --# @app.get( "/customers", tags=["Customers"], response_model=List[schema.Customer], response_model_exclude={"date_of_birth",'signup_date'}, # 集合形式, in case we need to exclude a field from response # response_model_exclude=["date_of_birth",'signup_date'],# 数组形式 # response_model_exclude_none=True , # usefull if response json is too big and we want to hide nulls to make it smaller summary="Gets all customers", response_description="A list containing all the customers" ) def get_customers( db: Session = Depends(get_db) ): return crud.get_customers(db) @app.get( "/customer/{customer_id}", tags=["Customers"], # a way to group api calls in the docs page response_model=List[schema.Customer], summary="Gets a single customer based on customer_id", response_description="A single customer based on the provided ID", responses={404: {"model": None, "description": "Customer ID not found"}} ) def get_customer(customer_id: int = Path( ..., title="Customer ID", description="Customer unique indetifier", gt=0 ), db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): """ Multiline comment """ result = crud.get_customer(db, customer_id) if not result: return Response( 'Customer not found', media_type="text/plain", status_code=HTTP_404_NOT_FOUND ) return result @app.post("/customer/", tags=["Customers"], # a way to group api calls in the docs page response_model=schema.CustomerInput, summary="Create a customer", response_description="Newly created customer", status_code = status.HTTP_201_CREATED ) def create_customer(customer: schema.CustomerInput, db: Session = Depends(get_db), #,auth: bool = Depends(is_authenticated) ): if not crud.get_loyalty_level_count(db, customer.level_id) > 0: raise HTTPException(status_code=404, detail=str(customer.level_id) + " is not a valid loyalty level id.") result = crud.create_customer(db, customer) return result @app.put("/customer/", tags=["Customers"], response_model=schema.Customer, summary="Update a single customer", response_description="Updated the customer", status_code = status.HTTP_200_OK ) def update_customer(customer: schema.Customer, db: Session = Depends(get_db), #,auth: bool = Depends(is_authenticated) ): if not crud.get_loyalty_level_count(db, customer.level_id) > 0: raise HTTPException(status_code=404, detail=str(customer.level_id) + " is not a valid loyalty level.") result = crud.update_customer(db, customer) if isinstance(result, model.Customer): return result else: if result == 404: raise HTTPException( status_code=404, detail="Could not find a customer with key (customer_id=" + str(customer.customer_id) + ")", headers={"X-Error": "Some error goes here"}, ) @app.delete("/customer/{customer_id}", tags=["Customers"], response_model=schema.Customer, summary="Delete a single customer based customer_id - cascading (all associated records will be deleted)", response_description="Deleted the customer and all the associated records", status_code = status.HTTP_200_OK ) def delete_customer(customer_id: int = Path( ..., title="Customer ID", description="Customer unique indetifier", gt=0 ), db: Session = Depends(get_db) ): result = crud.delete_customer(db, customer_id) if isinstance(result, model.Customer): return result else: if result == 404: raise HTTPException( status_code=404, detail="Could not find a customer with key (customer_id=" + str(customer_id) + ")", headers={"X-Error": "Some error goes here"}, ) # -- Purchase --# @app.get( "/purchases", tags=["Purchases"], response_model=List[schema.Purchase], summary="Gets all purchases", response_description="A list containing all the purchases" ) def get_purchases( db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): return crud.get_purchases(db) @app.get( "/purchase/{purchase_id}", tags=["Purchases"], response_model=List[schema.Purchase], summary="Gets a single purchase based on purchase_id", response_description="A single purchase based on the provided ID", responses={404: {"model": None, "description": "Purchase ID not found"}} ) def get_purchase(purchase_id: int = Path( ..., title="Purchase ID", description="Purchase unique indetifier", gt=0 ), db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): """ Multiline comment """ result = crud.get_purchase(db, purchase_id) if not result: return Response( 'No purchases found', media_type="text/plain", status_code=HTTP_404_NOT_FOUND ) return result @app.get( "/purchases/{customer_id}", tags=["Purchases"], response_model=List[schema.Purchase], summary="Gets a list of purchases based on customer_id", response_description="A list of purchases based on the provided ID", responses={404: {"model": None, "description": "Customer ID not found"}} ) def get_purchase(customer_id: int = Path( ..., title="Purchase ID", description="Purchase unique indetifier", gt=0 ), db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): """ Multiline comment """ result = crud.get_purchase_based_on_customer_id(db, customer_id) if not result: return Response( 'No purchases found', media_type="text/plain", status_code=HTTP_404_NOT_FOUND ) return result @app.post("/purchases/", tags=["Purchases"], # a way to group api calls in the docs page response_model=schema.Purchase, summary="Create a purchase", response_description="Newly created purchase", status_code = status.HTTP_201_CREATED ) def create_purchase(purchase: schema.PurchaseInput, db: Session = Depends(get_db), #,auth: bool = Depends(is_authenticated), ): result = crud.create_purchase(db, purchase) if isinstance(result, model.Purchase): return result else: if result == 404: raise HTTPException( status_code=404, detail="Integrity constrain violated. Parent key (customer_id=" + str(purchase.customer_id) + ") not found", headers={"X-Error": "Some error goes here"}, ) @app.put("/purchase/", tags=["Purchases"], response_model=schema.Purchase, summary="Update a single purchase", response_description="Updated the purchase", status_code = status.HTTP_200_OK ) def update_purchase(purchase: schema.Purchase, db: Session = Depends(get_db), #,auth: bool = Depends(is_authenticated) ): result = crud.update_purchase(db, purchase) if isinstance(result, model.Purchase): return result else: if result == 404: raise HTTPException( status_code=404, detail="Could not find a purchase card with purchase_id=" + str(purchase.purchase_id) + " and " + "customer_id=" + str(purchase.customer_id) + ")", headers={"X-Error": "Some error goes here"}, ) @app.delete("/purchase/{purchase_id}", tags=["Purchases"], response_model=schema.Purchase, summary="Delete a single purchase based on card_id", response_description="Deleted a single purchase based on card_id", status_code = status.HTTP_200_OK ) def delete_purchase(purchase_id: int = Path( ..., title="Purchase ID", description="Purchase unique indetifier", gt=0 ), db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): result = crud.delete_purchase(db, purchase_id) if isinstance(result, model.Purchase): return result else: if result == 404: raise HTTPException( status_code=404, detail="Could not find a purchase with key (card_id=" + str(purchase_id) + ")", headers={"X-Error": "Some error goes here"}, ) # -- LoyaltyLevel --# @app.get( "/loyalty_levels", tags=["LoyaltyLevels"], response_model=List[schema.LoyaltyLevel], summary="Gets all loyalty levels", response_description="A list containing all the loyalty levels" ) def get_loyalty_levels( db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): return crud.get_loyalty_levels(db) @app.get( "/loyalty_level/{level_id}", tags=["LoyaltyLevels"], response_model=List[schema.LoyaltyLevel], summary="Gets a single loyalty level based on level_id", response_description="A single loyalty level based on the provided ID", responses={404: {"model": None, "description": "Level ID not found"}} ) def get_loyalty_level(level_id: str = Path( ..., title="Loyalty level ID", description="Unique loyalty level indetifier", max_length=2 ), db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): result = crud.get_loyalty_level(db, level_id) if not result: return Response( 'Loyalty level not found', media_type="text/plain", status_code=HTTP_404_NOT_FOUND ) return result @app.post("/loyalty_level/", tags=["LoyaltyLevels"], response_model=schema.LoyaltyLevel, summary="Create a loyalty level", response_description="Newly created loyalty level", status_code = status.HTTP_201_CREATED ) def create_loyalty_level(loyalty_level: schema.LoyaltyLevel, db: Session = Depends(get_db), #,auth: bool = Depends(is_authenticated) ): result = crud.create_loyalty_level(db, loyalty_level) return result @app.put("/loyalty_level/", tags=["LoyaltyLevels"], response_model=schema.LoyaltyLevel, summary="Update a single loyalty level", response_description="Updated the loyalty level", status_code = status.HTTP_200_OK ) def update_loyalty_level(loyalty_level: schema.LoyaltyLevel, db: Session = Depends(get_db), #,auth: bool = Depends(is_authenticated) ): # if not crud.get_loyalty_level_count(db, loyalty_level.level_id) > 0: # raise HTTPException(status_code=404, detail=str(loyalty_level.level_id) + " is not a valid loyalty level.") result = crud.update_loyalty_level(db, loyalty_level) if isinstance(result, model.LoyaltyLevel): return result else: if result == 404: raise HTTPException( status_code=404, detail="Could not find a loyalty level with key (level_id=" + str(loyalty_level.level_id) + ")", headers={"X-Error": "Some error goes here"}, ) @app.delete("/loyalty_level/{level_id}", tags=["LoyaltyLevels"], response_model=schema.LoyaltyLevel, summary="Delete a single loyalty level based level_id", response_description="Deleted the loyalty level", status_code = status.HTTP_200_OK ) def delete_loyalty_level(level_id: str = Path( ..., title="Loyalty level ID", description="Unique loyalty level indetifier", max_length=2 ), db: Session = Depends(get_db) #,auth: bool = Depends(is_authenticated) ): result = crud.delete_loyalty_level(db, level_id) if isinstance(result, model.LoyaltyLevel): return result else: if result == 404: raise HTTPException( status_code=404, detail="Could not find a loyalty level with key (level_id=" + str(level_id) + ")", headers={"X-Error": "Some error goes here"}, )
/Users/song/codelearn/FastAPI-Oracle-main/app/database.py
import os, urllib from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.engine.url import URL #import pyodbc from dotenv import load_dotenv, find_dotenv load_dotenv(find_dotenv()) # ORACLE # connect_url = URL( # "oracle+cx_oracle", # username=urllib.parse.quote_plus(str(os.environ.get('DB_USERNAME', 'DEFAULT_DB_USERNAME'))), # password=urllib.parse.quote_plus(str(os.environ.get('DB_PASSWORD', 'DEFAULT_DB_PASSWORD'))), # host=str(os.environ.get('DB_HOST', 'DEFAULT_DB_HOST')), # port=str(os.environ.get('DB_PORT', 'DEFAULT_DB_PORT')), # database=str(os.environ.get('DB_DATABASE', 'DEFAULT_DB_DATABASE')), # ) # Azure SQL Server # connect_url = URL.create( # "mssql+pyodbc", # username=urllib.parse.quote_plus(str(os.environ.get('DB_USERNAME', 'DEFAULT_DB_USERNAME'))), # password=urllib.parse.quote_plus(str(os.environ.get('DB_PASSWORD', 'DEFAULT_DB_PASSWORD'))), # host=urllib.parse.quote_plus(str(os.environ.get('DB_HOST', 'DEFAULT_DB_HOST'))), # database=str(os.environ.get('DB_DATABASE', 'DEFAULT_DB_DATABASE')), # query={ # "driver": "ODBC Driver 17 for SQL Server" # }, # ) # engine = create_engine(connect_url, max_identifier_length=128) connect_url = 'sqlite:///test.db' engine = create_engine(connect_url) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base()
/Users/song/codelearn/FastAPI-Oracle-main/app/__init__.py
/Users/song/codelearn/FastAPI-Oracle-main/app/model.py
from numbers import Number from sqlalchemy import Sequence, Boolean, Column, ForeignKey, Integer, String, Date, Float from sqlalchemy.orm import relationship from .database import Base, engine class LoyaltyLevel(Base): __tablename__ = "loyalty_level" #__table_args__ = {'schema': 'db_schema_name'} level_id = Column(String(length=2), primary_key=True) description = Column(String(length=100)) discount = Column(Integer) customer = relationship("Customer", back_populates="loyalty_level") # one to many relationship class Purchase(Base): __tablename__ = "purchase" # __table_args__ = {'schema': 'db_schema_name'} purchase_id = Column(Integer, Sequence('purchase_id_seq'), primary_key=True) customer_id = Column(Integer, ForeignKey('customer.customer_id', ondelete="CASCADE"), nullable=False) purchase_name = Column(String(length=100)) purchase_date = Column(Date) # many-to-one customer = relationship("Customer", back_populates="purchase") class Customer(Base): __tablename__ = "customer" # __table_args__ = {'schema': 'db_schema_name'} customer_id = Column(Integer, Sequence('customer_id_seq'), primary_key=True) firstname = Column(String(length=100)) lastname = Column(String(length=100)) date_of_birth = Column(Date) level_id = Column(String(length=2), ForeignKey('loyalty_level.level_id')) signup_date = Column(Date) #one-to-one loyalty_level = relationship("LoyaltyLevel", back_populates="customer", uselist=False) #one-to-many purchase = relationship("Purchase", back_populates="customer", cascade="all, delete", passive_deletes=True,)
/Users/song/codelearn/FastAPI-Oracle-main/app/crud.py
from sqlalchemy.orm import Session from sqlalchemy import func, or_, exc from fastapi.encoders import jsonable_encoder from . import model, schema # -- Customer --# def get_customers(db: Session): return db.query(model.Customer).all() def get_customer(db: Session, customer_id: int): return db.query(model.Customer).filter( model.Customer.customer_id == customer_id ).all() def create_customer(db: Session, customer: schema.CustomerInput): db_item = model.Customer(firstname=customer.firstname, lastname=customer.lastname, date_of_birth=customer.date_of_birth, level_id=customer.level_id, signup_date=customer.signup_date ) # we can also populate the model using a shortcut: # db_item = model.Customer(**customer.dict()) db.add(db_item) db.commit() db.refresh(db_item) return db_item def update_customer(db: Session, customer: schema.Customer): existing_customer = db.query(model.Customer).filter(model.Customer.customer_id == customer.customer_id).first() if existing_customer: db.query(model.Customer).filter(model.Customer.customer_id == existing_customer.customer_id).update(customer.dict()) db.commit() return existing_customer else: return 404 def delete_customer(db: Session, customer_id: int): existing_customer = db.query(model.Customer).filter(model.Customer.customer_id == customer_id).first() if existing_customer: db.query(model.Customer).filter(model.Customer.customer_id == customer_id).delete() db.commit() return existing_customer else: return 404 # -- LoyaltyLevel --# def get_loyalty_levels(db: Session): return db.query(model.LoyaltyLevel).all() def get_loyalty_level(db: Session, level_id: int): return db.query(model.LoyaltyLevel).filter( model.LoyaltyLevel.level_id == level_id ).all() def get_loyalty_level_count(db: Session, level_id: int): return db.query(model.LoyaltyLevel).filter( model.LoyaltyLevel.level_id == level_id ).count() def create_loyalty_level(db: Session, loyalty_level: schema.LoyaltyLevel): db_item = model.LoyaltyLevel(**loyalty_level.dict()) db.add(db_item) db.commit() db.refresh(db_item) return db_item def update_loyalty_level(db: Session, loyalty_level: schema.LoyaltyLevel): existing_loyalty_level = db.query(model.LoyaltyLevel).filter(model.LoyaltyLevel.level_id == loyalty_level.level_id).first() if existing_loyalty_level: db.query(model.LoyaltyLevel).filter(model.LoyaltyLevel.level_id == existing_loyalty_level.level_id).update(loyalty_level.dict()) db.commit() return existing_loyalty_level else: return 404 def delete_loyalty_level(db: Session, level_id: int): existing_loyalty_level = db.query(model.LoyaltyLevel).filter(model.LoyaltyLevel.level_id == level_id).first() if existing_loyalty_level: db.query(model.LoyaltyLevel).filter(model.LoyaltyLevel.level_id == level_id).delete() db.commit() return existing_loyalty_level else: return 404 # -- Purchase --# def get_purchases(db: Session): return db.query(model.Purchase).all() def get_purchase(db: Session, purchase_id: int): return db.query(model.Purchase).filter( model.Purchase.purchase_id == purchase_id ).all() def get_purchase_based_on_customer_id(db: Session, customer_id: int): return db.query(model.Purchase).filter( model.Purchase.customer_id == customer_id ).all() def create_purchase(db: Session, purchase: schema.PurchaseInput): db_item = model.Purchase(**purchase.dict()) try: db.add(db_item) db.commit() db.refresh(db_item) return db_item except exc.IntegrityError: db.rollback() return 404 def update_purchase(db: Session, purchase: schema.Purchase): existing_purchase = db.query(model.Purchase).filter(model.Purchase.customer_id == purchase.customer_id, model.Purchase.purchase_id == purchase.purchase_id).first() if existing_purchase: db.query(model.Purchase).filter(model.Purchase.purchase_id == existing_purchase.purchase_id).update(purchase.dict()) db.commit() return existing_purchase else: return 404 def delete_purchase(db: Session, purchase_id: int): existing_purchase = db.query(model.Purchase).filter(model.Purchase.purchase_id == purchase_id).first() if existing_purchase: db.query(model.Purchase).filter(model.Purchase.purchase_id == existing_purchase.purchase_id).delete() db.commit() return existing_purchase else: return 404
/Users/song/codelearn/FastAPI-Oracle-main/app/schema.py
from ast import LtE, Num from typing import List, Optional from unicodedata import numeric from fastapi import Query from pydantic import BaseModel from datetime import date class LoyaltyLevel(BaseModel): level_id: str = Query( ..., title="Loyalty level ID", description="Loyalty level ID", max_length=2, ) description: Optional[str] = Query( None, title="Loyalty level description", description="The description of the Loyalty level", max_length=100, ) discount: Optional[int] = Query( 0, title="Loyalty discount percentage", description="Loyalty discount percentage", lte=100 ) class Config: orm_mode = True class Customer(BaseModel): customer_id: int = Query( ..., title="Customer ID", description="The ID of the customer", gt=0, ) firstname: Optional[str] = Query( None, title="Customer's first name", description="The first name of the customer", max_length=100, ) lastname: Optional[str] = Query( None, title="Customer's last name", description="The last name of the customer", max_length=100, ) date_of_birth: Optional[date] = Query( None, title="Date of birth", description="Customer's date of birth", ) level_id: str = Query( ..., title="Loyalty level ID", description="Loyalty level ID", max_length=2, ) signup_date: Optional[date] = Query( None, title="Sign up date", description="Customer's sign up date", ) class Config: orm_mode = True # a copy of the Customer model but without the customer_id. We create this for POST requests validation # this is done since we don't need to specify a the key (customer_id) whern creating a customer (since it's a sequence) class CustomerInput(BaseModel): firstname: Optional[str] = Query( None, title="Customer's first name", description="The first name of the customer", max_length=100, ) lastname: Optional[str] = Query( None, title="Customer's last name", description="The last name of the customer", max_length=100, ) date_of_birth: Optional[date] = Query( None, title="Date of birth", description="Customer's date of birth", ) level_id: str = Query( ..., title="Loyalty level ID", description="Loyalty level ID", max_length=2, ) signup_date: Optional[date] = Query( None, title="Sign up date", description="Customer's sign up date", ) class Config: orm_mode = True class Purchase(BaseModel): purchase_id: int = Query( ..., title="Purchase ID", description="The ID of the purchase", gt=0, ) customer_id: int = Query( ..., title="Customer ID FK", description="Customer ID FK", gt=0, ) purchase_name: Optional[str] = Query( None, title="Purchase name", description="The name of the purchase", max_length=100, ) purchase_date: Optional[date] = Query( None, title="Purchase date", description="The date of the purchase", ) class Config: orm_mode = True class PurchaseInput(BaseModel): customer_id: int = Query( ..., title="Customer ID FK", description="Customer ID FK", gt=0, ) purchase_name: Optional[str] = Query( None, title="Purchase name", description="The name of the purchase", max_length=100, ) purchase_date: Optional[date] = Query( None, title="Purchase date", description="The date of the purchase", ) class Config: orm_mode = True
FastAPI Oracle CRUD application
Introduction
This is a simple Python FastAPI test application that works with an Oracle database.
Summary
The goal of this project was to learn a bit more about FastAPI, SQLAlchemy, Oracle Database and Docker.
Once 'HelloWorld' started working, I wanted to experiment with something a bit more complex. Specifically, CRUD API which handles Foreign Keys, One-To-One and One-To-Many cascading relationships using SQLAlchemy and Oracle database behind it. Finally, I wanted to run the whole thing in a Docker.
Database diagram
Create database tables
The app will automatically create all 3 tables with sample data in the database on startup and will automatically drop them on shutdown
Installing and running in Docker
Tested on OS X 12.2
Asumptions
- Python 3.8 or higher is installed
- Docker is installed
After checking out the repo, in terminal, cd to the project root directory
Build Docker image using the Dockerfile.oracle
file
You will need to provide db_username, db_password, db_host, db_port and db_database as arguments when building the Docker
docker build -f ./Dockerfile.oracle -t fastapi-oracle-image . \ --build-arg DB_USERNAME="<db_username>" \ --build-arg DB_PASSWORD="<db_password>" \ --build-arg DB_HOST="<db_host>" \ --build-arg DB_PORT="<db_port>" \ --build-arg DB_DATABASE="<db_database>"
Running the server instance in Docker
Run the newly created Docker image in a fastapi-oracle-container
container
docker run -d --name fastapi-oracle-container -p 80:80 fastapi-oracle-image
To stop the container
docker stop fastapi-oracle-container
Installing and running in VirtualEnv (for development)
Tested on OS X 12.2
Asumptions
- Python 3.8 or higher is installed
- Oracle client libraries are installed. These can be downloaded from: https://www.oracle.com/database/technologies/instant-client.html
https://www.oracle.com/database/technologies/instant-client/downloads.html
After checking out the repo, in terminal, cd to the project root directory
Create virtual environment for the app
python3 -m venv .
Activate the virtual environment
source ./bin/activate
Install required packages from properties.txt
pip install --no-cache-dir --upgrade -r ./requirements.txt
Set database connection configuration
Rename the template.env
file to .env
mv template.env .env
Edit the .env
file in the project folder
emacs .env
By filling in the following variables:
DB_USERNAME="" DB_PASSWORD="" DB_HOST="" DB_PORT= DB_DATABASE=""
Running the server instance in VirtualEnv
Start the app
uvicorn main:app --reload
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战