编写Perl的Result模块管理HTTP错误,求核心实现指导
Hey there, let's work through your Result module and error handling setup step by step. I see you're trying to build a Perl module to manage HTTP errors, so let's break down what's working, what can be improved, and how to integrate this cleanly with your App.pm code.
First: Refining the Result.pm Core Error Handling
Your current generateError function works, but it can be made more maintainable, flexible, and aligned with Perl OO best practices. Here's an optimized version with key improvements explained:
package Result; use strict; use warnings; use Log::Log4perl; # Initialize logger (assumes you've configured Log::Log4perl elsewhere in your app) my $logger = Log::Log4perl->get_logger(__PACKAGE__); sub new { my $class = shift; my %params = @_; # Validate required parameters to avoid invalid Result objects die "Missing required 'code' parameter for Result->new" unless exists $params{code}; my $self = { code => $params{code}, value => $params{value}, # Optional: store context data like IDs, user info }; bless $self, $class; return $self; } # Encapsulate internal state with accessors (avoids direct hash key access) sub code { my $self = shift; if (@_) { $self->{code} = shift; $logger->debug("Updated Result HTTP code to " . $self->{code}); } return $self->{code}; } sub value { my $self = shift; $self->{value} = shift if @_; return $self->{value}; } sub generateError { my $self = shift; my %params = @_; my $code = $self->code; # Replace messy elsif chains with a maintainable hash of error messages my %standard_messages = ( 200 => "Your request was executed successfully!", 400 => "Bad request: Please verify your input and try again.", 403 => "Access denied: You don't have permission to access this resource.", 404 => "Resource not found: We couldn't locate what you're looking for.", 500 => "Internal server error: Our team has been notified, please try again later.", 504 => "Gateway timeout: Your request took too long to process, please retry." ); # Allow custom message overrides for specific scenarios my $message = $params{custom_message} || $standard_messages{$code} || "Unknown error code: $code"; # Log based on error severity (errors = 4xx/5xx, success = 2xx) if ($code >= 400) { $logger->error("HTTP $code: $message" . ($self->value ? " | Context: " . Dumper($self->value) : "")); } else { $logger->info("HTTP $code: $message"); } return $message; } 1;
Key Improvements:
- Encapsulation: Added accessor methods (
code,value) instead of directly accessing$self->{code}. This makes future changes to the module's internal structure easier. - Maintainable Error Messages: Swapped the long
elsifchain with a hash—adding new HTTP codes only requires a new hash entry, not more conditional logic. - Parameter Validation: The
newmethod now checks for the requiredcodeparameter to prevent invalid Result objects. - Flexibility: Supports custom error messages via the
custom_messageparameter ingenerateError. - Better Logging: Uses your existing
Log::Log4perlsetup to log errors with context (if you providevaluedata like project IDs or user IDs). - Fixed Syntax: Corrected the missing semicolon in your original 404 error message.
Second: Integrating Result.pm with App.pm
Your App.pm currently redirects to error pages, but we can use the Result module to standardize error handling and add context. First, add use Result; at the top of App.pm, then update your template method:
sub template { my $self = shift; my $query = $self->query; my $id = $query->param('id'); my $session = $self->param('session'); my $profile = $session->param('profile'); my $Project = Project->newFromId($id); if (!$Project or $Project eq 'NOT_FOUND') { # Create a 404 Result with context (project ID) my $error = Result->new(code => 404, value => { project_id => $id }); my $error_msg = $error->generateError; # Option 1: Redirect to your existing notfound handler with the error message return $self->redirect("?rm=notfound&error=" . URI::Escape::uri_escape($error_msg)); # Option 2: Render an error template directly with the Result object # return $self->processtmpl('error.tmpl', { error => $error }); } if (!$profile->{'uid'} or $Project->{'userId'} != $profile->{'uid'}) { # Use 403 Forbidden (correct HTTP code for permission issues, not 404) my $error = Result->new(code => 403, value => { project_id => $id, user_id => $profile->{'uid'} }); my $error_msg = $error->generateError(custom_message => "You don't have permission to view this project."); return $self->redirect("?rm=accessdenied&error=" . URI::Escape::uri_escape($error_msg)); } # Rest of your existing code... my $mailContent = from_json($Project->{'mail'}); my $templateContent = formatTemplate($Project->{'template'}); my $infos = [ { 'ID' => $id, 'TEMPLATE' => $templateContent, 'MAILSUBJECT' => $mailContent->{'subject'}, 'MAILBODY' => $mailContent->{'body'} } ]; if ($mailContent->{'type'} eq 'text') { $infos->[0]{'MAILTEXT'} = 1; } else { $infos->[0]{'MAILHTML'} = 1; } # Optional: Log the successful request with Result my $success = Result->new(code => 200, value => { project_id => $id }); $success->generateError; return $self->processtmpl('template.tmpl', $infos); }
Integration Notes:
- Correct HTTP Codes: Replaced the incorrect 404 redirect for permission issues with a 403 Forbidden code—this aligns with HTTP standards and makes debugging easier.
- Contextual Errors: Added
valuedata (like project/user IDs) to Result objects, so your logs will have more detail about what went wrong. - Flexible Error Handling: Choose between redirecting to existing handlers (with escaped error messages) or rendering an error template directly with the Result object.
Bonus Best Practices
- Structured Responses: If building an API, modify
generateErrorto return JSON instead of plain text (e.g.,return to_json({ code => $code, message => $message, details => $self->value })). - Exception Handling: Add a
throwmethod to Result:
Then usesub throw { my $self = shift; die $self->generateError(@_); }Try::Tinyin App.pm to catch exceptions cleanly. - Unit Tests: Write tests with
Test::Moreto verify Result returns the correct messages for every HTTP code you support.
内容的提问来源于stack exchange,提问作者Maestro

