I am writing a RESTful web service using ASP.NET MVC 3. I am using the basic GET/POST/PUT mappings for Retrieve/Create/Update actions, respectively. It works perfectly for one of my domain types but I’m implementing the exact same set of actions for another type and I’m getting a 401 Unauthorized response from the local ASP.NET development server.
First, here is the relevant section of the RegisterArea method in MyApiAreaRegistration.cs where the routes are created (I have removed the Update methods for brevity):
// student detail
context.MapRoute(
"StudentDetailV1",
"MyApi/v1/family/{email}/student/{id}",
new { controller = "Student", action = "StudentDetail" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
// create student
context.MapRoute(
"CreateStudentV1",
"MyApi/v1/family/{email}/student",
new { controller = "Student", action = "CreateStudent" },
new { httpMethod = new HttpMethodConstraint("POST") }
);
// family detail
context.MapRoute(
"FamilyDetailV1",
"MyApi/v1/family/{email}",
new { controller = "Student", action = "FamilyDetail" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
// create family
context.MapRoute(
"CreateFamilyV1",
"MyApi/v1/family",
new { controller = "Student", action = "CreateFamily" },
new { httpMethod = new HttpMethodConstraint("POST") }
);
And in the StudentController:
/// <summary>
/// return json representation of FamilyDto
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
[HttpGet]
public ActionResult FamilyDetail(string email)
{
Family f = _studentDataAccess.GetFamilyByEmail(email.Trim());
if (f == null)
{
return HttpNotFound();
}
FamilyDto familyDto = FamilyDtoAssembler.GetDtoFromFamily(f);
return Json(familyDto, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public ActionResult CreateFamily(FamilyDto familyDto)
{
if (string.IsNullOrWhiteSpace(familyDto.Email))
{
return new HttpStatusCodeResult(409, "Email address is required.");
}
// check for already existing family with that email.
Family f = _studentDataAccess.GetFamilyByEmail(familyDto.Email.Trim());
if (f != null)
{
return new HttpStatusCodeResult(409, "Email address must be unique.");
}
// turn family into a Family object with Parents
Family family = FamilyDtoAssembler.GetFamilyFromDto(familyDto);
// save family via dal (username = family email)
_studentDataAccess.CreateFamily(family, family.Email);
// return redirect to family detail
return RedirectToRoute("FamilyDetailV1", new { email = family.Email });
}
[HttpPost]
public ActionResult CreateStudent(string email, StudentDto studentDto)
{
if (string.IsNullOrWhiteSpace(email))
{
return HttpNotFound();
}
Family family = _studentDataAccess.GetFamilyByEmail(email.Trim());
if (family == null)
{
return HttpNotFound();
}
Student s = StudentDtoAssembler.GetStudentFromDto(_repository, studentDto);
s.Family = family;
_studentDataAccess.CreateStudent(s, email);
return RedirectToRoute("StudentDetailV1", new { email = email, id = s.Id });
}
[HttpGet]
public ActionResult StudentDetail(string email, int id)
{
Student s = _studentDataAccess.GetStudent(id);
if (s == null || s.Family.Email != email)
{
return HttpNotFound();
}
StudentDto studentDto = GeneralDtoAssembler.GetSingleStudentDto(s, s.MatsRegistrations);
return Json(studentDto, JsonRequestBehavior.AllowGet);
}
Now when I use Fiddler to craft a POST request to the “Create family” action, with the proper JSON request body to match the definition of the Family DTO, the action returns a 302 response that redirects to the Family Detail action for the newly created family. This works exactly how I want. However the problem is the “create student” action. When I step through it in the debugger it works properly and returns the RedirectToRoute result, however all I ever see in Fiddler is the following response:
HTTP/1.1 401 Unauthorized
Server: ASP.NET Development Server/10.0.0.0
Date: Tue, 17 Apr 2012 16:42:10 GMT
X-AspNet-Version: 4.0.30319
jsonerror: true
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 105
Connection: Close
{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"}
I have tried everything I can think of, from changing the authentication mode from “Windows” to “None” in web.config, to re-ordering the routes, to returning a RedirectToAction instead of RedirectToRoute, and nothing works. Note that if I just return a straight up Json result from CreateStudent rather than a RedirectToRoute result, then that works properly (200 status and I see the result just fine). However I can’t figure out why CreateStudent and CreateFamily are behaving differently and it’s been driving me nuts for 2 days now. Ha;lp?
The problem was the static URL segments in the student routes. I changed
StudentDetailV1to"MyApi/v1/family/{email}/{id}"and now it works properly.