Last active
September 10, 2020 03:55
-
-
Save davetransom/f70f5cb178ec0881fefc63c1a4fd08dd to your computer and use it in GitHub Desktop.
ShortGuid Model Binding (MVC or WebApi) example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Configure binding provider for MVC during the general setup, e.g. | |
// ... AreaRegistration.RegisterAllAreas(); | |
ModelBinderProviders.BinderProviders.Add(new MyProject.ModelBinders.Mvc.ShortGuidModelBinderProvider()); | |
// ... FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MvcSampleController : System.Web.Mvc.Controller | |
{ | |
/// <summary> | |
/// GET /watch/00amyWGct0y_ze4lIsj2Mw?token=Xy0MVKupFES9NpmZ9TiHcw | |
/// | |
/// ``` | |
/// [Details] | |
/// VideoId: `00amyWGct0y_ze4lIsj2Mw` => `c9a646d3-9c61-4cb7-bfcd-ee2522c8f633` | |
/// Token : `Xy0MVKupFES9NpmZ9TiHcw` => `540c2d5f-a9ab-4414-bd36-9999f5388773 | |
/// ``` | |
/// </summary> | |
/// <param name="videoId"></param> | |
/// <param name="token"></param> | |
/// <returns></returns> | |
[Route("watch/{videoId}")] | |
public ActionResult WatchVideo( | |
// url path param | |
ShortGuid? videoId = null, | |
// query string | |
ShortGuid? token = null | |
) | |
{ | |
string result = $@"[Details] | |
VideoId: `{videoId}` => `{videoId?.Guid.ToString() ?? "Invalid"}` | |
Token : `{token}` => `{token?.Guid.ToString() ?? "None"} | |
"; | |
return Content(result, "text/plain"); | |
} | |
public class SampleArgsModel | |
{ | |
public ShortGuid? ChannelId { get; set; } | |
} | |
/// <summary> | |
/// GET /watch?channelId=00amyWGct0y_ze4lIsj2Mw | |
/// | |
/// ``` | |
/// ChannelId: `00amyWGct0y_ze4lIsj2Mw` => `c9a646d3-9c61-4cb7-bfcd-ee2522c8f633` | |
/// ``` | |
/// </summary> | |
/// <param name="args"></param> | |
/// <returns></returns> | |
[Route("watch")] | |
public ActionResult WatchChannel( | |
SampleArgsModel args | |
) | |
{ | |
return Content($"ChannelId: `{args.ChannelId}` => `{args.ChannelId?.Guid.ToString() ?? "Invalid"}`", "text/plain"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CSharpVitamins; | |
using System; | |
using System.Web.Mvc; | |
namespace MyProject.ModelBinders.Mvc | |
{ | |
/// <summary> | |
/// An MVC model binder to convert 22 charactater URL-safe Base64 into <see cref="ShortGuid"/> instances. | |
/// </summary> | |
public sealed class ShortGuidModelBinder : IModelBinder | |
{ | |
/// <summary> | |
/// Binds the model to a value by using the specified controller context and binding context. | |
/// </summary> | |
/// <param name="controllerContext"></param> | |
/// <param name="bindingContext"></param> | |
/// <returns></returns> | |
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) | |
{ | |
var result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); | |
if (null == result?.AttemptedValue) | |
return null; | |
// Two options exist for parsing the incoming value | |
// 1. TryParse which will attempt a) 22 char ShortGuid format, then normal Guid parsing, and outputs a | |
// ShortGuid in this case. This will accept both formats e.g."00amyWGct0y_ze4lIsj2Mw" as well as | |
// "c9a646d3-9c61-4cb7-bfcd-ee2522c8f633". | |
/* | |
if (ShortGuid.TryParse(result.AttemptedValue, out ShortGuid parsed)) | |
return parsed; | |
*/ | |
// 2. TryDecode which will only decocde the 22 char ShortGuid format, but outputs a Guid. This will | |
// only accept "00amyWGct0y_ze4lIsj2Mw". | |
if (ShortGuid.TryDecode(result.AttemptedValue, out Guid decoded)) | |
return new ShortGuid(decoded); | |
return null; | |
} | |
} | |
/// <summary> | |
/// Provider for MVC Model Binding. | |
/// </summary> | |
/// <example> | |
/// <code> | |
/// // MVC general configuration. | |
/// ModelBinderProviders.BinderProviders.Add(new ShortGuidModelBinderProvider()); | |
/// </code> | |
/// </example> | |
public sealed class ShortGuidModelBinderProvider : IModelBinderProvider | |
{ | |
ShortGuidModelBinder binder = new ShortGuidModelBinder(); | |
/// <summary> | |
/// Returns the model binder for the specified type. | |
/// <para>Supports <see cref="ShortGuid"/> and nullable `ShortGuid?`.</para> | |
/// </summary> | |
/// <param name="modelType"></param> | |
/// <returns></returns> | |
public IModelBinder GetBinder(Type modelType) | |
{ | |
if (modelType == typeof(ShortGuid) || modelType == typeof(ShortGuid?)) | |
return binder; | |
// Could possibly extend to also run normal Guids through here too, though YMMV. | |
return null; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[RoutePrefix("api")] | |
public class WebApiSampleController : System.Web.Http.ApiController | |
{ | |
/// <summary> | |
/// GET /api/watch/FEx1sZbSD0ugmgMAF_RGHw | |
/// ``` | |
/// { | |
/// previewId: "FEx1sZbSD0ugmgMAF_RGHw", | |
/// guid: "b1754c14-d296-4b0f-a09a-030017f4461f" | |
/// } | |
/// ``` | |
/// /// </summary> | |
/// <param name="previewId"></param> | |
/// <returns></returns> | |
[HttpGet] | |
[Route("watch/{previewId}")] | |
public HttpResponseMessage Watch( | |
[ModelBinder(typeof(ShortGuidModelBinder))] ShortGuid previewId | |
) | |
{ | |
return Request.CreateResponse(HttpStatusCode.OK, new | |
{ | |
PreviewId = previewId.Value, | |
Guid = previewId.Guid, | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CSharpVitamins; | |
using System; | |
using System.Web.Http.Controllers; | |
using System.Web.Http.ModelBinding; | |
namespace MyProject.ModelBinders.WebApi | |
{ | |
/// <summary> | |
/// A WebApi model binder to convert 22 charactater URL-safe Base64 into <see cref="ShortGuid"/> instances. | |
/// </summary> | |
public sealed class ShortGuidModelBinder : IModelBinder | |
{ | |
/// <summary> | |
/// Binds the model to a value by using the specified controller context and binding context. | |
/// </summary> | |
/// <param name="actionContext"></param> | |
/// <param name="bindingContext"></param> | |
/// <returns></returns> | |
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) | |
{ | |
var result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); | |
if (null == result) | |
return false; | |
// You can decide if you want this try/catch/throw here. | |
try | |
{ | |
// Two options exist for parsing the incoming value | |
// 1. TryParse which will attempt a) 22 char ShortGuid format, then normal Guid parsing, and outputs a | |
// ShortGuid in this case. This will accept both formats e.g."00amyWGct0y_ze4lIsj2Mw" as well as | |
// "c9a646d3-9c61-4cb7-bfcd-ee2522c8f633". | |
/* | |
if (ShortGuid.TryParse(result.AttemptedValue, out ShortGuid parsed)) | |
{ | |
bindingContext.Model = parsed; | |
return true; | |
} | |
*/ | |
// 2. TryDecode which will only decocde the 22 char ShortGuid format, but outputs a Guid. This will | |
// only accept "00amyWGct0y_ze4lIsj2Mw". | |
if (ShortGuid.TryDecode(result.AttemptedValue, out Guid decoded)) | |
{ | |
bindingContext.Model = new ShortGuid(decoded); | |
return true; | |
} | |
} | |
catch (Exception ex) | |
{ | |
throw new ArgumentException($"An invalid short-guid value for `{bindingContext.ModelName}` was provided.", ex); | |
} | |
return false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment