需求:开发Phoenix框架/api/api_endpoints/接口以列出公开API端点
Nice question! I’ve tackled this exact scenario when building Phoenix APIs, and while there’s no built-in Phoenix function for this, it’s straightforward to roll your own solution. Here’s how I’d approach it:
Step 1: Mark Your Public Routes
First, you need a way to identify which routes are "public." The easiest way is to add a public: true metadata flag directly in your router. Open lib/my_app_web/router.ex and update your routes like this:
defmodule MyAppWeb.Router do use MyAppWeb, :router pipeline :api do plug :accepts, ["json"] end scope "/api", MyAppWeb do pipe_through :api # Public endpoints (marked with public: true) get "/users", UserController, :index, public: true, description: "List all users" post "/users", UserController, :create, public: true, description: "Create a new user" # Private endpoints (no public flag) get "/users/:id", UserController, :show put "/users/:id", UserController, :update end end
I also added an optional description field to make the endpoint list more useful—feel free to skip that if you don’t need it.
Step 2: Create the Endpoint Controller
Next, make a controller to handle the /api/api_endpoints request. Create lib/my_app_web/controllers/api_endpoint_controller.ex with this code:
defmodule MyAppWeb.ApiEndpointController do use MyAppWeb, :controller def index(conn, _params) do public_routes = MyAppWeb.Router |> Phoenix.Router.routes() |> Enum.filter(&is_public_route?/1) |> Enum.map(&format_route/1) json(conn, %{public_api_endpoints: public_routes}) end # Helper to check if a route is marked as public defp is_public_route?(route) do Keyword.get(route.opts, :public, false) end # Helper to format route data into a readable structure defp format_route(route) do %{ method: route.verb |> Atom.to_string() |> String.upcase(), path: route.path, action: "#{Module.split(route.controller) |> List.last()}.#{route.action}", description: Keyword.get(route.opts, :description, "No description provided") } end end
This code fetches all routes from your router, filters out the non-public ones, and formats the remaining routes into a clean JSON structure.
Step 3: Add the Route for Your Endpoint
Don’t forget to add the endpoint itself to your router—and mark it as public, of course! Add this line to your /api scope in router.ex:
get "/api_endpoints", ApiEndpointController, :index, public: true
Alternative: Filter by Pipeline
If you prefer grouping public routes with a dedicated pipeline instead of individual flags, you can adjust the logic. First, define a :public_api pipeline:
pipeline :public_api do plug :accepts, ["json"] end scope "/api", MyAppWeb do pipe_through :public_api # All routes here are public get "/users", UserController, :index get "/api_endpoints", ApiEndpointController, :index end scope "/api", MyAppWeb do pipe_through :api # Private routes here get "/users/:id", UserController, :show end
Then update the is_public_route? helper in your controller to check the pipeline:
defp is_public_route?(route) do :public_api in route.pipelines end
Testing It Out
Start your Phoenix server and hit GET http://localhost:4000/api/api_endpoints—you should get a JSON response with all your public routes, formatted nicely.
You can extend this further by adding more metadata (like request/response schemas) or even generating basic OpenAPI/Swagger docs from the route data if needed.
内容的提问来源于stack exchange,提问作者bexley

