go-create-chi-handler

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Create Chi Handler

Go 生成Chi HTTP处理器

Generate Chi HTTP handler implementations for a Go backend.
为Go后端生成Chi HTTP处理器实现。

Handler Structure

处理器结构

Location:
internal/modules/<module>/http/chi/handler/<resource>_handler.go
go
package handler

import (
	"net/http"
	"strconv"

	"github.com/cristiano-pacheco/bricks/pkg/http/request"
	"github.com/cristiano-pacheco/bricks/pkg/http/response"
	"github.com/cristiano-pacheco/bricks/pkg/logger"
	"github.com/cristiano-pacheco/pingo/internal/modules/<module>/http/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/<module>/usecase"
	"github.com/go-chi/chi/v5"
)

type ResourceHandler struct {
	resourceCreateUseCase *usecase.ResourceCreateUseCase
	resourceListUseCase   *usecase.ResourceListUseCase
	resourceUpdateUseCase *usecase.ResourceUpdateUseCase
	resourceDeleteUseCase *usecase.ResourceDeleteUseCase
	errorHandler          response.ErrorHandler
	logger                logger.Logger
}

func NewResourceHandler(
	resourceCreateUseCase *usecase.ResourceCreateUseCase,
	resourceListUseCase   *usecase.ResourceListUseCase,
	resourceUpdateUseCase *usecase.ResourceUpdateUseCase,
	resourceDeleteUseCase *usecase.ResourceDeleteUseCase,
	errorHandler response.ErrorHandler,
	logger logger.Logger,
) *ResourceHandler {
	return &ResourceHandler{
		resourceCreateUseCase: resourceCreateUseCase,
		resourceListUseCase:   resourceListUseCase,
		resourceUpdateUseCase: resourceUpdateUseCase,
		resourceDeleteUseCase: resourceDeleteUseCase,
		errorHandler:          errorHandler,
		logger:                logger,
	}
}
位置
internal/modules/<module>/http/chi/handler/<resource>_handler.go
go
package handler

import (
	"net/http"
	"strconv"

	"github.com/cristiano-pacheco/bricks/pkg/http/request"
	"github.com/cristiano-pacheco/bricks/pkg/http/response"
	"github.com/cristiano-pacheco/bricks/pkg/logger"
	"github.com/cristiano-pacheco/pingo/internal/modules/<module>/http/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/<module>/usecase"
	"github.com/go-chi/chi/v5"
)

type ResourceHandler struct {
	resourceCreateUseCase *usecase.ResourceCreateUseCase
	resourceListUseCase   *usecase.ResourceListUseCase
	resourceUpdateUseCase *usecase.ResourceUpdateUseCase
	resourceDeleteUseCase *usecase.ResourceDeleteUseCase
	errorHandler          response.ErrorHandler
	logger                logger.Logger
}

func NewResourceHandler(
	resourceCreateUseCase *usecase.ResourceCreateUseCase,
	resourceListUseCase   *usecase.ResourceListUseCase,
	resourceUpdateUseCase *usecase.ResourceUpdateUseCase,
	resourceDeleteUseCase *usecase.ResourceDeleteUseCase,
	errorHandler response.ErrorHandler,
	logger logger.Logger,
) *ResourceHandler {
	return &ResourceHandler{
		resourceCreateUseCase: resourceCreateUseCase,
		resourceListUseCase:   resourceListUseCase,
		resourceUpdateUseCase: resourceUpdateUseCase,
		resourceDeleteUseCase: resourceDeleteUseCase,
		errorHandler:          errorHandler,
		logger:                logger,
	}
}

DTOs (Data Transfer Objects)

DTO(数据传输对象)

Request and response DTOs are defined in
internal/modules/<module>/http/dto/<resource>_dto.go
.
Typical DTO structure:
go
package dto

type CreateResourceRequest struct {
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}

type CreateResourceResponse struct {
	ID     uint64 `json:"id"`
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}

type UpdateResourceRequest struct {
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}

type ResourceResponse struct {
	ID     uint64 `json:"id"`
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}
Key points:
  • DTOs live in the HTTP transport layer, separate from use case inputs or models
  • Use JSON tags for serialization
  • Keep DTOs focused on HTTP contract, not domain logic
请求和响应DTO定义在
internal/modules/<module>/http/dto/<resource>_dto.go
中。
典型DTO结构
go
package dto

type CreateResourceRequest struct {
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}

type CreateResourceResponse struct {
	ID     uint64 `json:"id"`
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}

type UpdateResourceRequest struct {
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}

type ResourceResponse struct {
	ID     uint64 `json:"id"`
	Field1 string `json:"field1"`
	Field2 int    `json:"field2"`
}
核心要点:
  • DTO位于HTTP传输层,与用例输入或模型分离
  • 使用JSON标签进行序列化
  • DTO需聚焦于HTTP契约,而非领域逻辑

Handler Method Patterns

处理器方法模式

List (GET /resource)

列表(GET /resource)

go
// @Summary		List resources
// @Description	Retrieves all resources
// @Tags		Resources
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Success		200	{object}	response.Envelope[[]dto.ResourceResponse]	"Successfully retrieved resources"
// @Failure		401	{object}	errs.Error	"Invalid credentials"
// @Failure		500	{object}	errs.Error	"Internal server error"
// @Router		/api/v1/resources [get]
func (h *ResourceHandler) ListResources(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	output, err := h.resourceListUseCase.Execute(ctx)
	if err != nil {
		h.logger.Error("failed to list resources", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	resources := make([]dto.ResourceResponse, len(output.Resources))
	for i, resource := range output.Resources {
		resources[i] = dto.ResourceResponse{
			ID:   resource.ID,
			Name: resource.Name,
			// ... map other fields
		}
	}

	err = response.JSON(w, http.StatusOK, resources, http.Header{})
	if err != nil {
		h.logger.Error("failed to write response", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}
}
go
// @Summary		列出资源
// @Description	获取所有资源
// @Tags		资源
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Success		200	{object}	response.Envelope[[]dto.ResourceResponse]	"成功获取资源列表"
// @Failure		401	{object}	errs.Error	"凭证无效"
// @Failure		500	{object}	errs.Error	"内部服务器错误"
// @Router		/api/v1/resources [get]
func (h *ResourceHandler) ListResources(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	output, err := h.resourceListUseCase.Execute(ctx)
	if err != nil {
		h.logger.Error("failed to list resources", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	resources := make([]dto.ResourceResponse, len(output.Resources))
	for i, resource := range output.Resources {
		resources[i] = dto.ResourceResponse{
			ID:   resource.ID,
			Name: resource.Name,
			// ... 映射其他字段
		}
	}

	err = response.JSON(w, http.StatusOK, resources, http.Header{})
	if err != nil {
		h.logger.Error("failed to write response", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}
}

Create (POST /resource)

创建(POST /resource)

go
// @Summary		Create resource
// @Description	Creates a new resource
// @Tags		Resources
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		request	body	dto.CreateResourceRequest	true	"Resource data"
// @Success		201	{object}	response.Envelope[dto.CreateResourceResponse]	"Successfully created resource"
// @Failure		422	{object}	errs.Error	"Invalid request format or validation error"
// @Failure		401	{object}	errs.Error	"Invalid credentials"
// @Failure		500	{object}	errs.Error	"Internal server error"
// @Router		/api/v1/resources [post]
func (h *ResourceHandler) CreateResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	var createRequest dto.CreateResourceRequest

	err := request.ReadJSON(w, r, &createRequest)
	if err != nil {
		h.logger.Error("failed to parse request body", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceCreateInput{
		Name: createRequest.Name,
		// ... map other fields
	}

	output, err := h.resourceCreateUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to create resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	createResponse := dto.CreateResourceResponse{
		ID:   output.ID,
		Name: output.Name,
		// ... map other fields
	}

	err = response.JSON(w, http.StatusCreated, createResponse, http.Header{})
	if err != nil {
		h.logger.Error("failed to write response", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}
}
go
// @Summary		创建资源
// @Description	创建新资源
// @Tags		资源
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		request	body	dto.CreateResourceRequest	true	"资源数据"
// @Success		201	{object}	response.Envelope[dto.CreateResourceResponse]	"成功创建资源"
// @Failure		422	{object}	errs.Error	"请求格式无效或验证错误"
// @Failure		401	{object}	errs.Error	"凭证无效"
// @Failure		500	{object}	errs.Error	"内部服务器错误"
// @Router		/api/v1/resources [post]
func (h *ResourceHandler) CreateResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	var createRequest dto.CreateResourceRequest

	err := request.ReadJSON(w, r, &createRequest)
	if err != nil {
		h.logger.Error("failed to parse request body", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceCreateInput{
		Name: createRequest.Name,
		// ... 映射其他字段
	}

	output, err := h.resourceCreateUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to create resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	createResponse := dto.CreateResourceResponse{
		ID:   output.ID,
		Name: output.Name,
		// ... 映射其他字段
	}

	err = response.JSON(w, http.StatusCreated, createResponse, http.Header{})
	if err != nil {
		h.logger.Error("failed to write response", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}
}

Update (PUT /resource/:id)

更新(PUT /resource/:id)

go
// @Summary		Update resource
// @Description	Updates an existing resource
// @Tags		Resources
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		id		path	int	true	"Resource ID"
// @Param		request	body	dto.UpdateResourceRequest	true	"Resource data"
// @Success		204		"Successfully updated resource"
// @Failure		422	{object}	errs.Error	"Invalid request format or validation error"
// @Failure		401	{object}	errs.Error	"Invalid credentials"
// @Failure		404	{object}	errs.Error	"Resource not found"
// @Failure		500	{object}	errs.Error	"Internal server error"
// @Router		/api/v1/resources/{id} [put]
func (h *ResourceHandler) UpdateResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	var updateRequest dto.UpdateResourceRequest

	err := request.ReadJSON(w, r, &updateRequest)
	if err != nil {
		h.logger.Error("failed to parse request body", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		h.logger.Error("invalid resource ID", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceUpdateInput{
		ID:   id,
		Name: updateRequest.Name,
		// ... map other fields
	}

	err = h.resourceUpdateUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to update resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	response.NoContent(w)
}
go
// @Summary		更新资源
// @Description	更新已有资源
// @Tags		资源
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		id		path	int	true	"资源ID"
// @Param		request	body	dto.UpdateResourceRequest	true	"资源数据"
// @Success		204		"成功更新资源"
// @Failure		422	{object}	errs.Error	"请求格式无效或验证错误"
// @Failure		401	{object}	errs.Error	"凭证无效"
// @Failure		404	{object}	errs.Error	"资源不存在"
// @Failure		500	{object}	errs.Error	"内部服务器错误"
// @Router		/api/v1/resources/{id} [put]
func (h *ResourceHandler) UpdateResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	var updateRequest dto.UpdateResourceRequest

	err := request.ReadJSON(w, r, &updateRequest)
	if err != nil {
		h.logger.Error("failed to parse request body", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		h.logger.Error("invalid resource ID", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceUpdateInput{
		ID:   id,
		Name: updateRequest.Name,
		// ... 映射其他字段
	}

	err = h.resourceUpdateUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to update resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	response.NoContent(w)
}

Delete (DELETE /resource/:id)

删除(DELETE /resource/:id)

go
// @Summary		Delete resource
// @Description	Deletes an existing resource
// @Tags		Resources
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		id	path	int	true	"Resource ID"
// @Success		204		"Successfully deleted resource"
// @Failure		401	{object}	errs.Error	"Invalid credentials"
// @Failure		404	{object}	errs.Error	"Resource not found"
// @Failure		500	{object}	errs.Error	"Internal server error"
// @Router		/api/v1/resources/{id} [delete]
func (h *ResourceHandler) DeleteResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		h.logger.Error("invalid resource ID", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceDeleteInput{
		ID: id,
	}

	err = h.resourceDeleteUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to delete resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	response.NoContent(w)
}
go
// @Summary		删除资源
// @Description	删除已有资源
// @Tags		资源
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		id	path	int	true	"资源ID"
// @Success		204		"成功删除资源"
// @Failure		401	{object}	errs.Error	"凭证无效"
// @Failure		404	{object}	errs.Error	"资源不存在"
// @Failure		500	{object}	errs.Error	"内部服务器错误"
// @Router		/api/v1/resources/{id} [delete]
func (h *ResourceHandler) DeleteResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		h.logger.Error("invalid resource ID", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceDeleteInput{
		ID: id,
	}

	err = h.resourceDeleteUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to delete resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	response.NoContent(w)
}

Get by ID (GET /resource/:id)

详情(GET /resource/:id)

go
// @Summary		Get resource
// @Description	Retrieves a resource by ID
// @Tags		Resources
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		id	path	int	true	"Resource ID"
// @Success		200	{object}	response.Envelope[dto.ResourceResponse]	"Successfully retrieved resource"
// @Failure		401	{object}	errs.Error	"Invalid credentials"
// @Failure		404	{object}	errs.Error	"Resource not found"
// @Failure		500	{object}	errs.Error	"Internal server error"
// @Router		/api/v1/resources/{id} [get]
func (h *ResourceHandler) GetResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		h.logger.Error("invalid resource ID", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceGetInput{
		ID: id,
	}

	output, err := h.resourceGetUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to get resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	resourceResponse := dto.ResourceResponse{
		ID:   output.ID,
		Name: output.Name,
		// ... map other fields
	}

	err = response.JSON(w, http.StatusOK, resourceResponse, http.Header{})
	if err != nil {
		h.logger.Error("failed to write response", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}
}
go
// @Summary		获取资源详情
// @Description	通过ID获取资源
// @Tags		资源
// @Accept		json
// @Produce		json
// @Security 	BearerAuth
// @Param		id	path	int	true	"资源ID"
// @Success		200	{object}	response.Envelope[dto.ResourceResponse]	"成功获取资源详情"
// @Failure		401	{object}	errs.Error	"凭证无效"
// @Failure		404	{object}	errs.Error	"资源不存在"
// @Failure		500	{object}	errs.Error	"内部服务器错误"
// @Router		/api/v1/resources/{id} [get]
func (h *ResourceHandler) GetResource(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	idStr := chi.URLParam(r, "id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		h.logger.Error("invalid resource ID", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	input := usecase.ResourceGetInput{
		ID: id,
	}

	output, err := h.resourceGetUseCase.Execute(ctx, input)
	if err != nil {
		h.logger.Error("failed to get resource", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}

	resourceResponse := dto.ResourceResponse{
		ID:   output.ID,
		Name: output.Name,
		// ... 映射其他字段
	}

	err = response.JSON(w, http.StatusOK, resourceResponse, http.Header{})
	if err != nil {
		h.logger.Error("failed to write response", logger.Error(err))
		h.errorHandler.Error(w, err)
		return
	}
}

Swagger Annotation Rules

Swagger注解规则

  1. @Summary: Brief action description (e.g., "List resources", "Create resource")
  2. @Description: Full description of what the endpoint does
  3. @Tags: Plural resource name (e.g., "Resources", "Contacts")
  4. @Accept: Always
    json
  5. @Produce: Always
    json
  6. @Security: Add
    BearerAuth
    if authentication required
  7. @Param: Define path params and request body
    • Path param:
      @Param id path int true "Resource ID"
    • Request body:
      @Param request body dto.CreateResourceRequest true "Resource data"
  8. @Success: Status code with response type
    • 200:
      {object} response.Envelope[dto.ResourceResponse]
    • 201:
      {object} response.Envelope[dto.CreateResourceResponse]
    • 204: No content, just description string
  9. @Failure: Common errors (401, 404, 422, 500) with
    {object} errs.Error
  10. @Router:
    /api/v1/resources/{id} [method]
  1. @Summary:简短的操作描述(如“列出资源”、“创建资源”)
  2. @Description:端点功能的完整描述
  3. @Tags:复数形式的资源名称(如“资源”、“联系人”)
  4. @Accept:始终为
    json
  5. @Produce:始终为
    json
  6. @Security:若需要认证则添加
    BearerAuth
  7. @Param:定义路径参数和请求体
    • 路径参数:
      @Param id path int true "资源ID"
    • 请求体:
      @Param request body dto.CreateResourceRequest true "资源数据"
  8. @Success:包含响应类型的状态码
    • 200:
      {object} response.Envelope[dto.ResourceResponse]
    • 201:
      {object} response.Envelope[dto.CreateResourceResponse]
    • 204: 无内容,仅描述字符串
  9. @Failure:常见错误(401、404、422、500)使用
    {object} errs.Error
  10. @Router
    /api/v1/resources/{id} [method]

Request/Response Mapping

请求/响应映射

Handler methods bridge HTTP requests/responses (DTOs from
internal/modules/<module>/http/dto
) with use case inputs/outputs.
处理器方法作为HTTP请求/响应(来自
internal/modules/<module>/http/dto
的DTO)与用例输入/输出之间的桥梁。

Request to Use Case Input

请求到用例输入

go
// Decode request
var req dto.CreateResourceRequest

err := request.ReadJSON(w, r, &req)
if err != nil {
	h.logger.Error("failed to parse request body", logger.Error(err))
	h.errorHandler.Error(w, err)
	return
}

// Map to use case input
input := usecase.ResourceCreateInput{
	Field1: req.Field1,
	Field2: req.Field2,
}
go
// 解析请求
var req dto.CreateResourceRequest

err := request.ReadJSON(w, r, &req)
if err != nil {
	h.logger.Error("failed to parse request body", logger.Error(err))
	h.errorHandler.Error(w, err)
	return
}

// 映射到用例输入
input := usecase.ResourceCreateInput{
	Field1: req.Field1,
	Field2: req.Field2,
}

Use Case Output to Response

用例输出到响应

go
// Execute use case
output, err := h.resourceCreateUseCase.Execute(ctx, input)
if err != nil {
	h.logger.Error("failed to create resource", logger.Error(err))
	h.errorHandler.Error(w, err)
	return
}

// Map to response DTO
response := dto.CreateResourceResponse{
	ID:     output.ID,
	Field1: output.Field1,
	Field2: output.Field2,
}
go
// 执行用例
output, err := h.resourceCreateUseCase.Execute(ctx, input)
if err != nil {
	h.logger.Error("failed to create resource", logger.Error(err))
	h.errorHandler.Error(w, err)
	return
}

// 映射到响应DTO
response := dto.CreateResourceResponse{
	ID:     output.ID,
	Field1: output.Field1,
	Field2: output.Field2,
}

Error Handling Pattern

错误处理模式

Every error follows this pattern:
go
if err != nil {
	h.logger.Error("descriptive error message", logger.Error(err))
	h.errorHandler.Error(w, err)
	return
}
Error messages:
  • "failed to parse request body" - JSON decode error
  • "invalid resource ID" - URL param parsing error
  • "failed to list resources" - List use case error
  • "failed to create resource" - Create use case error
  • "failed to update resource" - Update use case error
  • "failed to delete resource" - Delete use case error
  • "failed to get resource" - Get use case error
  • "failed to write response" - JSON response write error
所有错误均遵循以下模式:
go
if err != nil {
	h.logger.Error("descriptive error message", logger.Error(err))
	h.errorHandler.Error(w, err)
	return
}
错误消息:
  • "failed to parse request body" - JSON解析错误
  • "invalid resource ID" - URL参数解析错误
  • "failed to list resources" - 列表用例错误
  • "failed to create resource" - 创建用例错误
  • "failed to update resource" - 更新用例错误
  • "failed to delete resource" - 删除用例错误
  • "failed to get resource" - 详情用例错误
  • "failed to write response" - JSON响应写入错误

Fx Wiring

Fx依赖注入配置

Add to
internal/modules/<module>/fx.go
:
go
fx.Provide(handler.NewResourceHandler),
The handler is typically provided to the router, not exposed as a port.
添加到
internal/modules/<module>/fx.go
:
go
fx.Provide(handler.NewResourceHandler),
处理器通常会注入到路由中,而非作为端口暴露。

Critical Rules

关键规则

  1. Struct: Include all use cases,
    response.ErrorHandler
    , and
    logger.Logger
  2. Constructor: MUST return pointer
    *ResourceHandler
  3. Context: Always get from request:
    ctx := r.Context()
  4. Request decoding: Use
    request.ReadJSON(w, r, &dto)
    - declare variable first, then call ReadJSON
  5. URL params: Use
    chi.URLParam(r, "paramName")
    and
    strconv.ParseUint
    for IDs
  6. Error handling: Always log error and call
    h.errorHandler.Error(w, err)
    then return
  7. Response mapping: Map use case output to DTO, don't return use case outputs directly
  8. Success responses:
    • List/Get: Use
      response.JSON(w, http.StatusOK, data, http.Header{})
    • Create: Use
      response.JSON(w, http.StatusCreated, data, http.Header{})
    • Update/Delete: Use
      response.NoContent(w)
  9. Swagger: MUST add complete swagger annotations for every handler method
  10. No comments: Do not add redundant comments inside method bodies
  11. Validation: Run
    make lint
    and
    make update-swagger
    after generation
  1. 结构体:需包含所有用例依赖、
    response.ErrorHandler
    logger.Logger
  2. 构造函数:必须返回指针
    *ResourceHandler
  3. 上下文:始终从请求中获取:
    ctx := r.Context()
  4. 请求解析:使用
    request.ReadJSON(w, r, &dto)
    - 先声明变量,再调用ReadJSON
  5. URL参数:使用
    chi.URLParam(r, "paramName")
    strconv.ParseUint
    处理ID
  6. 错误处理:始终记录错误并调用
    h.errorHandler.Error(w, err)
    后返回
  7. 响应映射:将用例输出映射到DTO,不要直接返回用例输出
  8. 成功响应
    • 列表/详情:使用
      response.JSON(w, http.StatusOK, data, http.Header{})
    • 创建:使用
      response.JSON(w, http.StatusCreated, data, http.Header{})
    • 更新/删除:使用
      response.NoContent(w)
  9. Swagger:必须为每个处理器方法添加完整的Swagger注解
  10. 无冗余注释:方法体内不要添加多余注释
  11. 验证:生成后运行
    make lint
    make update-swagger

Workflow

工作流程

  1. Create handler struct with use case dependencies
  2. Implement constructor
    NewResourceHandler
  3. Implement handler methods following patterns above
  4. Add swagger annotations to all methods
  5. Add Fx wiring to module's
    fx.go
  6. Run
    make lint
    and
    make nilaway
    to verify the static tests
  7. Run
    make update-swagger
    to regenerate swagger docs
  1. 创建包含用例依赖的处理器结构体
  2. 实现构造函数
    NewResourceHandler
  3. 按照上述模式实现处理器方法
  4. 为所有方法添加Swagger注解
  5. 在模块的
    fx.go
    中添加Fx依赖注入配置
  6. 运行
    make lint
    make nilaway
    验证静态测试
  7. 运行
    make update-swagger
    重新生成Swagger文档