Skip to content

Instantly share code, notes, and snippets.

@wellwind
Last active July 30, 2019 01:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save wellwind/11a22b1a4f6fdad13f9e to your computer and use it in GitHub Desktop.
Save wellwind/11a22b1a4f6fdad13f9e to your computer and use it in GitHub Desktop.
ASP.NET WebApi自訂回傳訊息Demo
{
"StatusCode": 200,
"Result": {
Name = "Wellwind",
Age = 30
},
"Error": null
}
{
"StatusCode": 404,
"Result": null,
"Error": {
"ErrorId": "api_doesnt_exist",
"Message": "此Api不存在"
}
}
public class ApiResponse
{
public HttpStatusCode StatusCode { get; set; }
public object Result { get; set; }
public object Error { get; set; }
}
public class ApiException : Exception
{
public string ErrorId { get; set; }
public HttpStatusCode StatusCode { get; set; }
public ApiException()
: this("API呼叫錯誤")
{
}
public ApiException(string errorMessage)
: base(errorMessage)
{
ErrorId = "unknown_api_error";
StatusCode = HttpStatusCode.BadRequest;
}
}
public class ApiDoesntExistException : ApiException
{
public ApiDoesntExistException()
: base("此Api不存在")
{
ErrorId = "api_doesnt_exist";
StatusCode = HttpStatusCode.NotFound;
}
}
public class ApiResponseAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
if (actionExecutedContext.Exception != null)
{
return;
}
var result = new ApiResponse
{
StatusCode = actionExecutedContext.ActionContext.Response.StatusCode,
Result = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync().Result };
// 重新封裝回傳格式
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.StatusCode, result);
}
}
}
// add this line to enable custom api response message
config.Filters.Add(new ApiResponseAttribute());
public class ApiExceptionResponseAttribute : ExceptionFilterAttribute
{
/// <param name="actionExecutedContext">動作的內容。</param>
public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
// 將錯誤訊息轉成要回傳的ApiResponseResult
var errorApiResponseResult = exceptionToApiResponse(actionExecutedContext.Exception);
// 重新打包回傳的訊息
actionExecutedContext.Response =
actionExecutedContext.Request.CreateResponse(errorApiResponseResult.StatusCode, errorApiResponseResult);
}
private static ApiResponse exceptionToApiResponse(Exception exception)
{
var errorApiResponseResult = new ApiResponse();
if (exception is ApiException)
{
var apiException = exception as ApiException;
errorApiResponseResult.StatusCode = apiException.StatusCode;
errorApiResponseResult.Error = new
{
ErrorId = apiException.ErrorId,
Message = apiException.Message
};
}
else
{
errorApiResponseResult.StatusCode = HttpStatusCode.BadRequest;
errorApiResponseResult.Error = new
{
ErrorId = "",
Message = exception.Message
};
}
return errorApiResponseResult;
}
}
// add this line to enable custom api response message when exception thrown
config.Filters.Add(new ApiExceptionResponseAttribute());
public class ExceptionTestController : ApiController
{
public object Get()
{
throw new Exception("Test Exception Message...");
}
public object Post()
{
throw new ApiException("Test Api Exception Message...");
}
}
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_BeginRequest()
{
throw new Exception("thrown by Application_BeginRequest");
}
protected void Application_Error()
{
var exception = Server.GetLastError();
if (exception == null)
{
return;
}
object exceptionToSerialize = exception.InnerException ?? exception;
Response.ContentType = "text/json";
Response.Write(
JsonConvert.SerializeObject(
ExceptionUtils.ConvertToApiResponse((Exception)exceptionToSerialize)));
Response.End();
}
}
/// <summary>
/// HttpNotFound使用自訂Controller的ApiActionSelector
/// </summary>
public class HttpNotFoundAwareControllerActionSelector : ApiControllerActionSelector
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpNotFoundAwareControllerActionSelector"/> class.
/// </summary>
public HttpNotFoundAwareControllerActionSelector()
{
}
/// <summary>
/// 為 <see cref="T:System.Web.Http.Controllers.ApiControllerActionSelector" /> 選取動作。
/// </summary>
/// <param name="controllerContext">控制器內容。</param>
/// <returns>
/// 選取的動作。
/// </returns>
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
HttpActionDescriptor decriptor = null;
try
{
decriptor = base.SelectAction(controllerContext);
}
catch (HttpResponseException ex)
{
setErrorController(controllerContext, ex);
decriptor = base.SelectAction(controllerContext);
}
return decriptor;
}
private static void setErrorController(HttpControllerContext controllerContext, HttpResponseException ex)
{
var controllerName = "Error404";
var code = ex.Response.StatusCode;
var routeValues = controllerContext.RouteData.Values;
if (code != HttpStatusCode.NotFound && code != HttpStatusCode.MethodNotAllowed)
{
controllerName = "ErrorOthers";
routeValues["id"] = code;
}
routeValues["action"] = "Get";
controllerContext.Request.Method = HttpMethod.Get;
IHttpController httpController = new Error404Controller();
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration,
controllerName, httpController.GetType());
}
}
/// <summary>
/// HttpNotFound使用自訂Controller的ControllerSelector
/// </summary>
public class HttpNotFoundAwareDefaultHttpControllerSelector : DefaultHttpControllerSelector
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpNotFoundAwareDefaultHttpControllerSelector"/> class.
/// </summary>
/// <param name="configuration">設定。</param>
public HttpNotFoundAwareDefaultHttpControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
}
/// <summary>
/// 為指定的 <see cref="T:System.Net.Http.HttpRequestMessage" /> 選取 <see cref="T:System.Web.Http.Controllers.HttpControllerDescriptor" />。
/// </summary>
/// <param name="request">HTTP 要求的訊息。</param>
/// <returns>
/// 指定之 <see cref="T:System.Net.Http.HttpRequestMessage" /> 適用的 <see cref="T:System.Web.Http.Controllers.HttpControllerDescriptor" /> 執行個體。
/// </returns>
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
HttpControllerDescriptor decriptor = null;
try
{
decriptor = base.SelectController(request);
}
catch (HttpResponseException ex)
{
setErrorController(request, ex);
decriptor = base.SelectController(request);
}
return decriptor;
}
private static void setErrorController(HttpRequestMessage request, HttpResponseException ex)
{
var code = ex.Response.StatusCode;
var routeValues = request.GetRouteData().Values;
routeValues["controller"] = "Error";
if (code == HttpStatusCode.NotFound)
{
routeValues["controller"] = "Error404";
}
else
{
routeValues["controller"] = "ErrorOthers";
routeValues["id"] = code;
}
routeValues["action"] = "Get";
request.Method = HttpMethod.Get;
}
}
public class Error404Controller : ApiController
{
public object Get()
{
throw new Exception("找不到此API");
}
}
public class ErrorOthersController : ApiController
{
public object Get(int id)
{
return new HttpStatusCodeResult(id);
}
}
config.Services.Replace(typeof(IHttpControllerSelector), new HttpNotFoundAwareDefaultHttpControllerSelector(config));
config.Services.Replace(typeof(IHttpActionSelector), new HttpNotFoundAwareControllerActionSelector());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment