In HTTP 1.1, there actually is a status code (307) which indicates that the request should be repeated using the same method and post data.
As others have said, there is a potential for misuse here which may be why many frameworks stick to 301 and 302 in their abstractions. However, with proper understanding and responsible usage, you should be able to accomplish what you're looking for.
Note that according to the W3.org spec, when the METHOD
is not HEAD
or GET
, user agents should prompt the user before re-executing the request at the new location. You should also provide a note and a fallback mechanism for the user in case old user agents aren't sure what to do with a 307.
Using this form:
<form action="Test307.aspx" method="post">
<input type="hidden" name="test" value="the test" />
<input type="submit" value="test" />
</form>
And having Test307.aspx simply return 307 with the Location:http://google.com, Chrome 13 and Fiddler confirm that "test=the test" is indeed posted to Google. Of course the further response is a 405 since Google doesn't allow the POST, but it shows the mechanics.
For more information see List of HTTP status codes and the W3.org spec.
307 Temporary Redirect (since HTTP/1.1) In this occasion, the request
should be repeated with another URI, but future requests can still use
the original URI.2 In contrast to 303, the request method should not
be changed when reissuing the original request. For instance, a POST
request must be repeated using another POST request.
I know it may seem like you are converting objects back and forth all the time between your database objects, your data transfer objects, your client objects with validation logic and so on but I'd say that no, you're not doing anything wrong.
Each of these objects may represent the same unit of information, but they have very different responsibilities. The database object is your communication interface with the database and should be kept in the database layer since it may or may not have different database metadata annotations and/or unnecessary details about the database implementation in it.
Your data transfer object is the communication interface with your API consumers. These should be as clean as possible to facilitate easy consumption from different languages/platforms. This might impose certain restrictions on how these look and behave depending on what API consumers you wish to support.
Your client objects with validation logic is really not a part of your API project, they're part of your consumer project. These cannot be the same as the data transfer objects in this case since you are adding extra client-specific logic (in this case validation attributes) on them which the server knows nothing about (and should not know anything about!) You shouldn't count these objects as part of your API, because they're really not. They're highly consumer application specific and some applications that consume your API might actually not even need to create these object and could just as well survive on just your data transfer objects. For example, if you didn't have any need of validation you wouldn't need an extra layer of objects that are completely identical to your data transfer objects. Generally my strategy here is to evolve the project in stages, I start off by plainly using the data transfer objects as my consumer application objects and when I see the need for application specific handling of data (validation, implementing INotifyPropertyChanged, etc..) I do a quick refactoring and add in the new layer as needed.
To me, it seems like each of the three object types map very nicely to a single responsibility which is clean coding and good practice. Sadly, clean code and good practices sometimes means that you are writing a lot of extra code and jumping through extra hoops "just because". And while coding, it may be hard to appreciate the value that this is giving you - but as soon as you release your application and begin supporting it or adding new features for the next version then you will probably start appreciating that you took the time to separate these concerns properly in the first place. (Not to mention that you'll avoid certain problems that might occur due to improper separation of concerns - and since you have avoided these problems it might be hard to appreciate the mess that you can find yourself in when you have not separated your concerns.)
I also hate writing conversion code between different object types like this, but my solution is usually one of the following:
- Use a library that does most of the object conversion heavy lifting for you - for example, if you use C# you can use the fantastic AutoMapper library (http://automapper.org/). I believe that there's a couple of other libraries like this, but AutoMapper is the most powerful one I've seen so far.
- If you can't find a library help you with your object conversions, write a set of utility methods for converting between them. This might suck, but it's worth it in the long run, write the conversion method the first time you need to convert something - don't wait.
Best Answer
From a REST perspective the web conversation should go like this
The user fills in the form
The user identifies the problem with the form submission and fixes it
There is no need to introduce a redirect, and it would break the semantics of the conversation to do so.