From cac9e6e7605184f5679b1ebfbe5b5805191d9a53 Mon Sep 17 00:00:00 2001
From: John Olheiser <42128690+jolheiser@users.noreply.github.com>
Date: Mon, 18 Mar 2019 21:29:43 -0500
Subject: [PATCH] Updates to API 404 responses (#6077)

---
 modules/context/api.go                    | 29 ++++++++++++++++
 routers/api/v1/admin/user.go              |  2 +-
 routers/api/v1/api.go                     | 40 +++++++++++------------
 routers/api/v1/org/hook.go                |  2 +-
 routers/api/v1/org/member.go              |  6 ++--
 routers/api/v1/org/team.go                |  4 +--
 routers/api/v1/repo/branch.go             |  4 +--
 routers/api/v1/repo/collaborators.go      |  2 +-
 routers/api/v1/repo/file.go               |  8 ++---
 routers/api/v1/repo/git_ref.go            |  2 +-
 routers/api/v1/repo/hook.go               |  2 +-
 routers/api/v1/repo/issue.go              | 10 +++---
 routers/api/v1/repo/issue_comment.go      |  4 +--
 routers/api/v1/repo/issue_label.go        | 10 +++---
 routers/api/v1/repo/issue_tracked_time.go | 10 +++---
 routers/api/v1/repo/key.go                |  2 +-
 routers/api/v1/repo/label.go              |  4 +--
 routers/api/v1/repo/milestone.go          |  4 +--
 routers/api/v1/repo/pull.go               | 24 +++++++-------
 routers/api/v1/repo/release.go            |  6 ++--
 routers/api/v1/repo/release_attachment.go | 10 +++---
 routers/api/v1/repo/repo.go               |  4 +--
 routers/api/v1/user/app.go                |  2 +-
 routers/api/v1/user/follower.go           |  2 +-
 routers/api/v1/user/gpg_key.go            |  2 +-
 routers/api/v1/user/key.go                |  6 ++--
 routers/api/v1/user/star.go               |  2 +-
 routers/api/v1/user/user.go               |  2 +-
 routers/api/v1/user/watch.go              |  2 +-
 routers/api/v1/utils/hook.go              |  4 +--
 30 files changed, 120 insertions(+), 91 deletions(-)

diff --git a/modules/context/api.go b/modules/context/api.go
index b27ffcbc8c..7ec4d9036c 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -1,4 +1,5 @@
 // Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2019 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -6,6 +7,8 @@ package context
 
 import (
 	"fmt"
+	"net/url"
+	"path"
 	"strings"
 
 	"github.com/go-macaron/csrf"
@@ -140,3 +143,29 @@ func ReferencesGitRepo() macaron.Handler {
 		}
 	}
 }
+
+// NotFound handles 404s for APIContext
+// String will replace message, errors will be added to a slice
+func (ctx *APIContext) NotFound(objs ...interface{}) {
+	var message = "Not Found"
+	var errors []string
+	for _, obj := range objs {
+		if err, ok := obj.(error); ok {
+			errors = append(errors, err.Error())
+		} else {
+			message = obj.(string)
+		}
+	}
+
+	u, err := url.Parse(setting.AppURL)
+	if err != nil {
+		ctx.Error(500, "Invalid AppURL", err)
+		return
+	}
+	u.Path = path.Join(u.Path, "api", "swagger")
+	ctx.JSON(404, map[string]interface{}{
+		"message":           message,
+		"documentation_url": u.String(),
+		"errors":            errors,
+	})
+}
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index cf52d649ad..2e2a7fecb9 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -288,7 +288,7 @@ func DeleteUserPublicKey(ctx *context.APIContext) {
 
 	if err := models.DeletePublicKey(u, ctx.ParamsInt64(":id")); err != nil {
 		if models.IsErrKeyNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else if models.IsErrKeyAccessDenied(err) {
 			ctx.Error(403, "", "You do not have access to this key")
 		} else {
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 89d277233f..0b5c37a355 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -74,7 +74,7 @@ import (
 	api "code.gitea.io/sdk/gitea"
 
 	"github.com/go-macaron/binding"
-	macaron "gopkg.in/macaron.v1"
+	"gopkg.in/macaron.v1"
 )
 
 func sudo() macaron.Handler {
@@ -89,7 +89,7 @@ func sudo() macaron.Handler {
 				user, err := models.GetUserByName(sudo)
 				if err != nil {
 					if models.IsErrUserNotExist(err) {
-						ctx.Status(404)
+						ctx.NotFound()
 					} else {
 						ctx.Error(500, "GetUserByName", err)
 					}
@@ -124,7 +124,7 @@ func repoAssignment() macaron.Handler {
 			owner, err = models.GetUserByName(userName)
 			if err != nil {
 				if models.IsErrUserNotExist(err) {
-					ctx.Status(404)
+					ctx.NotFound()
 				} else {
 					ctx.Error(500, "GetUserByName", err)
 				}
@@ -141,7 +141,7 @@ func repoAssignment() macaron.Handler {
 				if err == nil {
 					context.RedirectToRepo(ctx.Context, redirectRepoID)
 				} else if models.IsErrRepoRedirectNotExist(err) {
-					ctx.Status(404)
+					ctx.NotFound()
 				} else {
 					ctx.Error(500, "LookupRepoRedirect", err)
 				}
@@ -160,7 +160,7 @@ func repoAssignment() macaron.Handler {
 		}
 
 		if !ctx.Repo.HasAccess() {
-			ctx.Status(404)
+			ctx.NotFound()
 			return
 		}
 	}
@@ -268,7 +268,7 @@ func reqOrgMembership() macaron.Handler {
 			if ctx.Org.Organization != nil {
 				ctx.Error(403, "", "Must be an organization member")
 			} else {
-				ctx.Status(404)
+				ctx.NotFound()
 			}
 			return
 		}
@@ -294,7 +294,7 @@ func reqOrgOwnership() macaron.Handler {
 			if ctx.Org.Organization != nil {
 				ctx.Error(403, "", "Must be an organization owner")
 			} else {
-				ctx.Status(404)
+				ctx.NotFound()
 			}
 			return
 		}
@@ -320,7 +320,7 @@ func orgAssignment(args ...bool) macaron.Handler {
 			ctx.Org.Organization, err = models.GetOrgByName(ctx.Params(":orgname"))
 			if err != nil {
 				if models.IsErrOrgNotExist(err) {
-					ctx.Status(404)
+					ctx.NotFound()
 				} else {
 					ctx.Error(500, "GetOrgByName", err)
 				}
@@ -332,7 +332,7 @@ func orgAssignment(args ...bool) macaron.Handler {
 			ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid"))
 			if err != nil {
 				if models.IsErrUserNotExist(err) {
-					ctx.Status(404)
+					ctx.NotFound()
 				} else {
 					ctx.Error(500, "GetTeamById", err)
 				}
@@ -344,36 +344,36 @@ func orgAssignment(args ...bool) macaron.Handler {
 
 func mustEnableIssues(ctx *context.APIContext) {
 	if !ctx.Repo.CanRead(models.UnitTypeIssues) {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 }
 
-func mustAllowPulls(ctx *context.Context) {
+func mustAllowPulls(ctx *context.APIContext) {
 	if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 }
 
-func mustEnableIssuesOrPulls(ctx *context.Context) {
+func mustEnableIssuesOrPulls(ctx *context.APIContext) {
 	if !ctx.Repo.CanRead(models.UnitTypeIssues) &&
 		!(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 }
 
-func mustEnableUserHeatmap(ctx *context.Context) {
+func mustEnableUserHeatmap(ctx *context.APIContext) {
 	if !setting.Service.EnableUserHeatmap {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 }
 
-func mustNotBeArchived(ctx *context.Context) {
+func mustNotBeArchived(ctx *context.APIContext) {
 	if ctx.Repo.Repository.IsArchived {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 }
@@ -683,8 +683,8 @@ func RegisterRoutes(m *macaron.Macaron) {
 			})
 		}, orgAssignment(false, true), reqToken(), reqOrgMembership())
 
-		m.Any("/*", func(ctx *context.Context) {
-			ctx.Error(404)
+		m.Any("/*", func(ctx *context.APIContext) {
+			ctx.NotFound()
 		})
 
 		m.Group("/admin", func() {
diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go
index 84559a958e..5825de2c1c 100644
--- a/routers/api/v1/org/hook.go
+++ b/routers/api/v1/org/hook.go
@@ -164,7 +164,7 @@ func DeleteHook(ctx *context.APIContext) {
 	hookID := ctx.ParamsInt64(":id")
 	if err := models.DeleteWebhookByOrgID(org.ID, hookID); err != nil {
 		if models.IsErrWebhookNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "DeleteWebhookByOrgID", err)
 		}
diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go
index b66d86c660..33c906ec6a 100644
--- a/routers/api/v1/org/member.go
+++ b/routers/api/v1/org/member.go
@@ -135,11 +135,11 @@ func IsMember(ctx *context.APIContext) {
 			} else if userToCheckIsMember {
 				ctx.Status(204)
 			} else {
-				ctx.Status(404)
+				ctx.NotFound()
 			}
 			return
 		} else if ctx.User.ID == userToCheck.ID {
-			ctx.Status(404)
+			ctx.NotFound()
 			return
 		}
 	}
@@ -177,7 +177,7 @@ func IsPublicMember(ctx *context.APIContext) {
 	if userToCheck.IsPublicMember(ctx.Org.Organization.ID) {
 		ctx.Status(204)
 	} else {
-		ctx.Status(404)
+		ctx.NotFound()
 	}
 }
 
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index a1916db00b..ca3de9f04e 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -242,7 +242,7 @@ func GetTeamMembers(ctx *context.APIContext) {
 		ctx.Error(500, "IsOrganizationMember", err)
 		return
 	} else if !isMember {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	team := ctx.Org.Team
@@ -391,7 +391,7 @@ func getRepositoryByParams(ctx *context.APIContext) *models.Repository {
 	repo, err := models.GetRepositoryByName(ctx.Org.Team.OrgID, ctx.Params(":reponame"))
 	if err != nil {
 		if models.IsErrRepoNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetRepositoryByName", err)
 		}
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index e8c965a814..e20eef6139 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -42,13 +42,13 @@ func GetBranch(ctx *context.APIContext) {
 		// if TreePath != "", then URL contained extra slashes
 		// (i.e. "master/subbranch" instead of "master"), so branch does
 		// not exist
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	branch, err := ctx.Repo.Repository.GetBranch(ctx.Repo.BranchName)
 	if err != nil {
 		if models.IsErrBranchNotExist(err) {
-			ctx.Error(404, "GetBranch", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetBranch", err)
 		}
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index 2f254f7103..be4a65f9c8 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -92,7 +92,7 @@ func IsCollaborator(ctx *context.APIContext) {
 	if isColab {
 		ctx.Status(204)
 	} else {
-		ctx.Status(404)
+		ctx.NotFound()
 	}
 }
 
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
index 762a0f25d9..929ed00d68 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -40,14 +40,14 @@ func GetRawFile(ctx *context.APIContext) {
 	//   200:
 	//     description: success
 	if ctx.Repo.Repository.IsEmpty {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 
 	blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
 	if err != nil {
 		if git.IsErrNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetBlobByPath", err)
 		}
@@ -124,7 +124,7 @@ func GetEditorconfig(ctx *context.APIContext) {
 	ec, err := ctx.Repo.GetEditorconfig()
 	if err != nil {
 		if git.IsErrNotExist(err) {
-			ctx.Error(404, "GetEditorconfig", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetEditorconfig", err)
 		}
@@ -134,7 +134,7 @@ func GetEditorconfig(ctx *context.APIContext) {
 	fileName := ctx.Params("filename")
 	def := ec.GetDefinitionForFilename(fileName)
 	if def == nil {
-		ctx.Error(404, "GetDefinitionForFilename", err)
+		ctx.NotFound(err)
 		return
 	}
 	ctx.JSON(200, def)
diff --git a/routers/api/v1/repo/git_ref.go b/routers/api/v1/repo/git_ref.go
index 7d60b8e0e7..ffdfc57b1d 100644
--- a/routers/api/v1/repo/git_ref.go
+++ b/routers/api/v1/repo/git_ref.go
@@ -89,7 +89,7 @@ func getGitRefsInternal(ctx *context.APIContext, filter string) {
 	}
 
 	if len(refs) == 0 {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 
diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go
index 369603694f..f01049721e 100644
--- a/routers/api/v1/repo/hook.go
+++ b/routers/api/v1/repo/hook.go
@@ -239,7 +239,7 @@ func DeleteHook(ctx *context.APIContext) {
 	//     "$ref": "#/responses/notFound"
 	if err := models.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
 		if models.IsErrWebhookNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "DeleteWebhookByRepoID", err)
 		}
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 27b3d93a75..08bc732530 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -146,7 +146,7 @@ func GetIssue(ctx *context.APIContext) {
 	issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -283,7 +283,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -412,7 +412,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -478,7 +478,7 @@ func StartIssueStopwatch(ctx *context.APIContext) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -547,7 +547,7 @@ func StopIssueStopwatch(ctx *context.APIContext) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index 3e6f04eb7a..fd085b66bc 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -268,7 +268,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
 	comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrCommentNotExist(err) {
-			ctx.Error(404, "GetCommentByID", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetCommentByID", err)
 		}
@@ -361,7 +361,7 @@ func deleteIssueComment(ctx *context.APIContext) {
 	comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrCommentNotExist(err) {
-			ctx.Error(404, "GetCommentByID", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetCommentByID", err)
 		}
diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go
index d496f5bde6..4c8ca8d523 100644
--- a/routers/api/v1/repo/issue_label.go
+++ b/routers/api/v1/repo/issue_label.go
@@ -44,7 +44,7 @@ func ListIssueLabels(ctx *context.APIContext) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -99,7 +99,7 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -171,7 +171,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -237,7 +237,7 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -303,7 +303,7 @@ func ClearIssueLabels(ctx *context.APIContext) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index 6c29b11b4b..d2ddb0e0c8 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -47,13 +47,13 @@ func ListTrackedTimes(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/TrackedTimeList"
 	if !ctx.Repo.Repository.IsTimetrackerEnabled() {
-		ctx.Error(404, "IsTimetrackerEnabled", "Timetracker is diabled")
+		ctx.NotFound("Timetracker is disabled")
 		return
 	}
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Error(404, "GetIssueByIndex", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -109,7 +109,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrIssueNotExist(err) {
-			ctx.Error(404, "GetIssueByIndex", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetIssueByIndex", err)
 		}
@@ -165,14 +165,14 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
 	user, err := models.GetUserByName(ctx.Params(":timetrackingusername"))
 	if err != nil {
 		if models.IsErrUserNotExist(err) {
-			ctx.Error(404, "GetUserByName", err)
+			ctx.NotFound(err)
 		} else {
 			ctx.Error(500, "GetUserByName", err)
 		}
 		return
 	}
 	if user == nil {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{
diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go
index 2ee1ce0098..e26ce8cf50 100644
--- a/routers/api/v1/repo/key.go
+++ b/routers/api/v1/repo/key.go
@@ -125,7 +125,7 @@ func GetDeployKey(ctx *context.APIContext) {
 	key, err := models.GetDeployKeyByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrDeployKeyNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetDeployKeyByID", err)
 		}
diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go
index df7a370041..f61751f3ab 100644
--- a/routers/api/v1/repo/label.go
+++ b/routers/api/v1/repo/label.go
@@ -87,7 +87,7 @@ func GetLabel(ctx *context.APIContext) {
 	}
 	if err != nil {
 		if models.IsErrLabelNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetLabelByRepoID", err)
 		}
@@ -172,7 +172,7 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) {
 	label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrLabelNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetLabelByRepoID", err)
 		}
diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go
index 88386c3efa..a76f601a21 100644
--- a/routers/api/v1/repo/milestone.go
+++ b/routers/api/v1/repo/milestone.go
@@ -78,7 +78,7 @@ func GetMilestone(ctx *context.APIContext) {
 	milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrMilestoneNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetMilestoneByRepoID", err)
 		}
@@ -169,7 +169,7 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) {
 	milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrMilestoneNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetMilestoneByRepoID", err)
 		}
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 9d3d9443f7..156ca83a19 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -136,7 +136,7 @@ func GetPullRequest(ctx *context.APIContext) {
 	pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrPullRequestNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetPullRequestByIndex", err)
 		}
@@ -231,7 +231,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 		milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
 		if err != nil {
 			if models.IsErrMilestoneNotExist(err) {
-				ctx.Status(404)
+				ctx.NotFound()
 			} else {
 				ctx.Error(500, "GetMilestoneByRepoID", err)
 			}
@@ -341,7 +341,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
 	pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrPullRequestNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetPullRequestByIndex", err)
 		}
@@ -438,7 +438,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
 	pr, err = models.GetPullRequestByIndex(ctx.Repo.Repository.ID, pr.Index)
 	if err != nil {
 		if models.IsErrPullRequestNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetPullRequestByIndex", err)
 		}
@@ -481,7 +481,7 @@ func IsPullRequestMerged(ctx *context.APIContext) {
 	pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
 	if err != nil {
 		if models.IsErrPullRequestNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetPullRequestByIndex", err)
 		}
@@ -491,7 +491,7 @@ func IsPullRequestMerged(ctx *context.APIContext) {
 	if pr.HasMerged {
 		ctx.Status(204)
 	}
-	ctx.Status(404)
+	ctx.NotFound()
 }
 
 // MergePullRequest merges a PR given an index
@@ -554,7 +554,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
 	}
 
 	if pr.Issue.IsClosed {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 
@@ -634,7 +634,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 		headBranch = headInfos[1]
 
 	} else {
-		ctx.Status(404)
+		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""
 	}
 
@@ -643,7 +643,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 	log.Info("Repo path: %s", ctx.Repo.GitRepo.Path)
 	// Check if base branch is valid.
 	if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
-		ctx.Status(404)
+		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""
 	}
 
@@ -651,7 +651,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 	headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID)
 	if !has && !isSameRepo {
 		log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
-		ctx.Status(404)
+		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""
 	}
 
@@ -674,13 +674,13 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 	}
 	if !perm.CanReadIssuesOrPulls(true) {
 		log.Trace("ParseCompareInfo[%d]: cannot create/read pull requests", baseRepo.ID)
-		ctx.Status(404)
+		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""
 	}
 
 	// Check if head branch is valid.
 	if !headGitRepo.IsBranchExist(headBranch) {
-		ctx.Status(404)
+		ctx.NotFound()
 		return nil, nil, nil, nil, "", ""
 	}
 
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index e622acb30c..1b5b19666c 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -46,7 +46,7 @@ func GetRelease(ctx *context.APIContext) {
 		return
 	}
 	if release.RepoID != ctx.Repo.Repository.ID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	if err := release.LoadAttributes(); err != nil {
@@ -241,7 +241,7 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) {
 	}
 	if err != nil && models.IsErrReleaseNotExist(err) ||
 		rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 
@@ -313,7 +313,7 @@ func DeleteRelease(ctx *context.APIContext) {
 	}
 	if err != nil && models.IsErrReleaseNotExist(err) ||
 		rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	if err := models.DeleteReleaseByID(id, ctx.User, false); err != nil {
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
index ea2be719f3..50107dd44e 100644
--- a/routers/api/v1/repo/release_attachment.go
+++ b/routers/api/v1/repo/release_attachment.go
@@ -57,7 +57,7 @@ func GetReleaseAttachment(ctx *context.APIContext) {
 		return
 	}
 	if attach.ReleaseID != releaseID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
@@ -98,7 +98,7 @@ func ListReleaseAttachments(ctx *context.APIContext) {
 		return
 	}
 	if release.RepoID != ctx.Repo.Repository.ID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	if err := release.LoadAttributes(); err != nil {
@@ -150,7 +150,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
 
 	// Check if attachments are enabled
 	if !setting.AttachmentEnabled {
-		ctx.Error(404, "AttachmentEnabled", errors.New("attachment is not enabled"))
+		ctx.NotFound("Attachment is not enabled")
 		return
 	}
 
@@ -262,7 +262,7 @@ func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptio
 		return
 	}
 	if attach.ReleaseID != releaseID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
@@ -319,7 +319,7 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
 		return
 	}
 	if attach.ReleaseID != releaseID {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index e5922e77dc..36e8380f44 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -472,7 +472,7 @@ func GetByID(ctx *context.APIContext) {
 	repo, err := models.GetRepositoryByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrRepoNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetRepositoryByID", err)
 		}
@@ -484,7 +484,7 @@ func GetByID(ctx *context.APIContext) {
 		ctx.Error(500, "AccessLevel", err)
 		return
 	} else if !perm.HasAccess() {
-		ctx.Status(404)
+		ctx.NotFound()
 		return
 	}
 	ctx.JSON(200, repo.APIFormat(perm.AccessMode))
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index f4870431c2..ef53e95d8b 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -112,7 +112,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
 	tokenID := ctx.ParamsInt64(":id")
 	if err := models.DeleteAccessTokenByID(tokenID, ctx.User.ID); err != nil {
 		if models.IsErrAccessTokenNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "DeleteAccessTokenByID", err)
 		}
diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go
index 284b7323c3..c2f0c6e651 100644
--- a/routers/api/v1/user/follower.go
+++ b/routers/api/v1/user/follower.go
@@ -113,7 +113,7 @@ func checkUserFollowing(ctx *context.APIContext, u *models.User, followID int64)
 	if u.IsFollowing(followID) {
 		ctx.Status(204)
 	} else {
-		ctx.Status(404)
+		ctx.NotFound()
 	}
 }
 
diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go
index 5301a0f2db..9d6579e413 100644
--- a/routers/api/v1/user/gpg_key.go
+++ b/routers/api/v1/user/gpg_key.go
@@ -90,7 +90,7 @@ func GetGPGKey(ctx *context.APIContext) {
 	key, err := models.GetGPGKeyByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrGPGKeyNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetGPGKeyByID", err)
 		}
diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go
index d8ab752b2b..8e0cde0535 100644
--- a/routers/api/v1/user/key.go
+++ b/routers/api/v1/user/key.go
@@ -42,7 +42,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *models.User {
 	user, err := models.GetUserByName(ctx.Params(name))
 	if err != nil {
 		if models.IsErrUserNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetUserByName", err)
 		}
@@ -165,7 +165,7 @@ func GetPublicKey(ctx *context.APIContext) {
 	key, err := models.GetPublicKeyByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrKeyNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetPublicKeyByID", err)
 		}
@@ -246,7 +246,7 @@ func DeletePublicKey(ctx *context.APIContext) {
 	//     "$ref": "#/responses/notFound"
 	if err := models.DeletePublicKey(ctx.User, ctx.ParamsInt64(":id")); err != nil {
 		if models.IsErrKeyNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else if models.IsErrKeyAccessDenied(err) {
 			ctx.Error(403, "", "You do not have access to this key")
 		} else {
diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go
index b0016399c8..62c820f8f4 100644
--- a/routers/api/v1/user/star.go
+++ b/routers/api/v1/user/star.go
@@ -96,7 +96,7 @@ func IsStarring(ctx *context.APIContext) {
 	if models.IsStaring(ctx.User.ID, ctx.Repo.Repository.ID) {
 		ctx.Status(204)
 	} else {
-		ctx.Status(404)
+		ctx.NotFound()
 	}
 }
 
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index cbc7bbc238..a44eb03c39 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -109,7 +109,7 @@ func GetInfo(ctx *context.APIContext) {
 	u, err := models.GetUserByName(ctx.Params(":username"))
 	if err != nil {
 		if models.IsErrUserNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetUserByName", err)
 		}
diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go
index 4afa18be2a..2ce0be2ff0 100644
--- a/routers/api/v1/user/watch.go
+++ b/routers/api/v1/user/watch.go
@@ -103,7 +103,7 @@ func IsWatching(ctx *context.APIContext) {
 			RepositoryURL: repositoryURL(ctx.Repo.Repository),
 		})
 	} else {
-		ctx.Status(404)
+		ctx.NotFound()
 	}
 }
 
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index 380dc886a5..f0c6b91a18 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -24,7 +24,7 @@ func GetOrgHook(ctx *context.APIContext, orgID, hookID int64) (*models.Webhook,
 	w, err := models.GetWebhookByOrgID(orgID, hookID)
 	if err != nil {
 		if models.IsErrWebhookNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetWebhookByOrgID", err)
 		}
@@ -39,7 +39,7 @@ func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*models.Webhook
 	w, err := models.GetWebhookByRepoID(repoID, hookID)
 	if err != nil {
 		if models.IsErrWebhookNotExist(err) {
-			ctx.Status(404)
+			ctx.NotFound()
 		} else {
 			ctx.Error(500, "GetWebhookByID", err)
 		}